wizards 0.6.2 → 0.7.1
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 -2
- package/apps/runtime/dist/agents/architect.js +7 -29
- package/apps/runtime/dist/agents/studio-assistant.js +7 -24
- package/apps/runtime/dist/cli/autostart.js +58 -12
- package/apps/runtime/dist/cli/autoupdate.js +221 -0
- package/apps/runtime/dist/cli/connect.js +6 -2
- package/apps/runtime/dist/cli/environment.js +5 -0
- package/apps/runtime/dist/cli/machine.js +7 -3
- package/apps/runtime/dist/cli/main.js +10 -3
- package/apps/runtime/dist/cli/menu.js +27 -8
- package/apps/runtime/dist/cli/processes.js +72 -0
- package/apps/runtime/dist/cli/setup.js +26 -26
- package/apps/runtime/dist/cli/start.js +18 -1
- package/apps/runtime/dist/cli/status.js +51 -17
- package/apps/runtime/dist/cli/uninstall-main.js +20 -0
- package/apps/runtime/dist/cli/uninstall.js +231 -0
- package/apps/runtime/dist/cli/update.js +25 -2
- package/apps/runtime/dist/cli/versions.js +106 -0
- package/apps/runtime/dist/docs/user/command-line.md +3 -2
- package/apps/runtime/dist/docs/user/data.md +1 -1
- package/apps/runtime/dist/docs/user/install.md +29 -6
- package/apps/runtime/dist/engine/input.js +4 -5
- package/apps/runtime/dist/engine/steps.js +3 -3
- package/apps/runtime/dist/engine/template.js +8 -13
- package/apps/runtime/dist/env.js +3 -20
- package/apps/runtime/dist/features.js +2 -2
- package/apps/runtime/dist/index.js +3 -1
- package/apps/runtime/dist/manage.js +7 -0
- package/apps/runtime/dist/models.js +12 -0
- package/apps/runtime/dist/sandbox/docker.js +0 -2
- package/apps/runtime/dist/sandbox/index.js +3 -10
- package/apps/runtime/dist/services/space-categories.js +3 -25
- package/apps/runtime/dist/tools/browser.js +2 -14
- package/apps/runtime/dist/tools/index.js +1 -19
- package/apps/runtime/dist/tools/mail.js +1 -4
- package/apps/runtime/dist/tools/shared.js +30 -3
- package/apps/runtime/dist/update.js +10 -19
- package/apps/web/dist/assets/AboutDialog-BJALish1.js +1 -0
- package/apps/web/dist/assets/{ChangelogDialog-BRiSYyIn.js → ChangelogDialog-CxmHNvq1.js} +1 -1
- package/apps/web/dist/assets/ChatView-C9ASPVJj.js +10 -0
- package/apps/web/dist/assets/EditorPage-BHhNEiKa.js +3 -0
- package/apps/web/dist/assets/{FlowPreview-CUOEzoeJ.js → FlowPreview-DvpjuQdC.js} +1 -1
- package/apps/web/dist/assets/{Harness-D3kTVsF5.js → Harness-Duq0wpfg.js} +2 -2
- package/apps/web/dist/assets/{NewWizardChannels-C3PGe_Zd.js → NewWizardChannels-CGFy67jk.js} +1 -1
- package/apps/web/dist/assets/NewWizardPage-DwBcRWJj.js +7 -0
- package/apps/web/dist/assets/{OnboardingPage-pOPB5TVZ.js → OnboardingPage-YwKzupPI.js} +1 -1
- package/apps/web/dist/assets/ProjectPage-DP1xMjbe.js +1 -0
- package/apps/web/dist/assets/PublicRunner-CECAuP0g.js +1 -0
- package/apps/web/dist/assets/{ReadOnly-CCxZvYrT.js → ReadOnly-BnnGcLWU.js} +1 -1
- package/apps/web/dist/assets/RunDrawer-C0vf8QAc.js +4 -0
- package/apps/web/dist/assets/{RunnerView-pKik3sdE.js → RunnerView-CBrZpjt5.js} +1 -1
- package/apps/web/dist/assets/SettingsPage-C0-mVcs5.js +1 -0
- package/apps/web/dist/assets/SetupPage-BGlYQREU.js +1 -0
- package/apps/web/dist/assets/{SharePage-CZQqp5JM.js → SharePage-IwxlV6WC.js} +1 -1
- package/apps/web/dist/assets/changelog-CPNPseoQ.js +1 -0
- package/apps/web/dist/assets/{esm-D4pSRQgT.js → esm-6TNBQNCD.js} +1 -1
- package/apps/web/dist/assets/index-DU-Onx-c.js +14 -0
- package/apps/web/dist/assets/{lock-DYnHCyRg.js → lock-HLZib0u9.js} +1 -1
- package/apps/web/dist/assets/{phone-DJawY1RF.js → phone-DsxbIcWi.js} +1 -1
- package/apps/web/dist/assets/settings-menu-BMMkIVP1.js +1 -0
- package/apps/web/dist/assets/ui-Brg2qM1A.js +137 -0
- package/apps/web/dist/assets/{upload-DL6NRa4h.js → upload-BaGy0ppG.js} +1 -1
- package/apps/web/dist/index.html +2 -2
- package/apps/web/dist/install.sh +30 -12
- package/apps/web/dist/mcp/flow.html +11 -11
- package/bin/wizards.mjs +17 -3
- package/node_modules/@engenty-wizards/shared/dist/definition.js +6 -17
- package/node_modules/@engenty-wizards/shared/dist/features.d.ts +6 -0
- package/node_modules/@engenty-wizards/shared/dist/features.js +12 -0
- package/node_modules/@engenty-wizards/shared/dist/numbers.d.ts +24 -0
- package/node_modules/@engenty-wizards/shared/dist/numbers.js +54 -0
- package/node_modules/@engenty-wizards/shared/dist/store.d.ts +7 -0
- package/node_modules/@engenty-wizards/shared/dist/store.js +28 -0
- package/package.json +1 -1
- package/apps/runtime/dist/sandbox/agentos.js +0 -191
- package/apps/web/dist/assets/AboutDialog-BrfbtjmR.js +0 -1
- package/apps/web/dist/assets/ChatView-_dYvuuUY.js +0 -10
- package/apps/web/dist/assets/EditorPage-CkRjTRMg.js +0 -3
- package/apps/web/dist/assets/NewWizardPage-B3ol4K43.js +0 -7
- package/apps/web/dist/assets/ProjectPage-Boyptzz_.js +0 -1
- package/apps/web/dist/assets/PublicRunner-DcETj9_E.js +0 -1
- package/apps/web/dist/assets/RunDrawer-hwruVTDG.js +0 -4
- package/apps/web/dist/assets/SettingsPage-BOe6NgFg.js +0 -1
- package/apps/web/dist/assets/SetupPage-81QxPnfE.js +0 -1
- package/apps/web/dist/assets/changelog-CtyrKHAc.js +0 -1
- package/apps/web/dist/assets/index-DnIvQFIg.js +0 -14
- package/apps/web/dist/assets/settings-menu-B_-_EAmK.js +0 -1
- package/apps/web/dist/assets/ui-rh8TaWvT.js +0 -137
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { join } from "node:path";
|
|
1
3
|
import * as p from "@clack/prompts";
|
|
2
4
|
import { updateStatus } from "../update.js";
|
|
3
5
|
import { accountMenu, accountState, describeAccount } from "./account.js";
|
|
4
|
-
import {
|
|
6
|
+
import { autostartForStatus, setAutostart } from "./autostart.js";
|
|
5
7
|
import { connectCommand } from "./connect.js";
|
|
6
|
-
import { command, packageVersion, readInstallNote } from "./home.js";
|
|
8
|
+
import { command, packageRoot, packageVersion, readInstallNote } from "./home.js";
|
|
7
9
|
import { setup } from "./setup.js";
|
|
8
10
|
import { open, runningRuntime, settings, start } from "./start.js";
|
|
9
11
|
import { restart, status, stop } from "./status.js";
|
|
@@ -13,7 +15,7 @@ async function look(paths) {
|
|
|
13
15
|
const { dataDir } = settings(paths);
|
|
14
16
|
const [running, atLogin, release, account] = await Promise.all([
|
|
15
17
|
runningRuntime(dataDir),
|
|
16
|
-
|
|
18
|
+
autostartForStatus(paths),
|
|
17
19
|
updateStatus().catch(() => null),
|
|
18
20
|
accountState(),
|
|
19
21
|
]);
|
|
@@ -45,7 +47,7 @@ function choices({ running, atLogin, release, account }) {
|
|
|
45
47
|
]
|
|
46
48
|
: [
|
|
47
49
|
{ value: "start", label: "Start in the background", hint: "and open the studio" },
|
|
48
|
-
{ value: "foreground", label: "Start
|
|
50
|
+
{ value: "foreground", label: "Start in this terminal", hint: "Ctrl-C stops it" },
|
|
49
51
|
];
|
|
50
52
|
options.push({
|
|
51
53
|
value: "update",
|
|
@@ -62,14 +64,18 @@ function choices({ running, atLogin, release, account }) {
|
|
|
62
64
|
options.push({
|
|
63
65
|
value: "connect",
|
|
64
66
|
label: "Connect AI apps",
|
|
65
|
-
hint: "
|
|
67
|
+
hint: "add your wizards as an MCP server in Claude, Cursor, Codex…",
|
|
66
68
|
}, {
|
|
67
69
|
value: "account",
|
|
68
70
|
label: "Account",
|
|
69
71
|
hint: account.kind === "linked"
|
|
70
72
|
? `signed in as ${describeAccount(account)}`
|
|
71
73
|
: describeAccount(account),
|
|
72
|
-
}, { value: "status", label: "Status", hint: "what’s installed, and any problems" }, { value: "setup", label: "Setup", hint: "account, AI client, ffmpeg, start at login" }, {
|
|
74
|
+
}, { value: "status", label: "Status", hint: "what’s installed, and any problems" }, { value: "setup", label: "Setup", hint: "account, AI client, ffmpeg, start at login" }, {
|
|
75
|
+
value: "uninstall",
|
|
76
|
+
label: "Uninstall",
|
|
77
|
+
hint: "remove it from this computer; asks about your data first",
|
|
78
|
+
}, { value: "help", label: "Help", hint: "every command" }, { value: "quit", label: "Quit", hint: running ? "engenty wizards keeps running" : undefined });
|
|
73
79
|
return options;
|
|
74
80
|
}
|
|
75
81
|
/** Whether it starts at login. Esc goes back to the menu. */
|
|
@@ -81,7 +87,11 @@ async function serviceSettings(paths, atLogin) {
|
|
|
81
87
|
message: "Start at login?",
|
|
82
88
|
initialValue: atLogin.on,
|
|
83
89
|
options: [
|
|
84
|
-
{
|
|
90
|
+
{
|
|
91
|
+
value: true,
|
|
92
|
+
label: "Yes",
|
|
93
|
+
hint: "runs in the background whenever you log in to this computer",
|
|
94
|
+
},
|
|
85
95
|
{ value: false, label: "No", hint: "only when you start it" },
|
|
86
96
|
],
|
|
87
97
|
});
|
|
@@ -112,7 +122,7 @@ function commands() {
|
|
|
112
122
|
}
|
|
113
123
|
/**
|
|
114
124
|
* `wizards` on a terminal: what runs, and a menu of what the commands do. The first time, the
|
|
115
|
-
* setup runs first. Returns the exit code; "Start
|
|
125
|
+
* setup runs first. Returns the exit code; "Start in this terminal" ends it with the runtime in the
|
|
116
126
|
* foreground.
|
|
117
127
|
*/
|
|
118
128
|
export async function menu(paths, setupOptions, help) {
|
|
@@ -175,6 +185,15 @@ export async function menu(paths, setupOptions, help) {
|
|
|
175
185
|
case "setup":
|
|
176
186
|
await setup(paths, setupOptions);
|
|
177
187
|
break;
|
|
188
|
+
case "uninstall": {
|
|
189
|
+
// Its own process, as `wizards uninstall` is: this one's modules write to the install.
|
|
190
|
+
const { status: code } = spawnSync(process.execPath, [join(packageRoot, "bin", "wizards.mjs"), "uninstall"], { stdio: "inherit" });
|
|
191
|
+
// 130: the person said no, so the menu goes on.
|
|
192
|
+
if (code !== 130) {
|
|
193
|
+
return code ?? 1;
|
|
194
|
+
}
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
178
197
|
case "help":
|
|
179
198
|
console.log(help);
|
|
180
199
|
break;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { sep } from "node:path";
|
|
3
|
+
/** Every process, as `ps` lists it; empty where there is no `ps` (Windows). */
|
|
4
|
+
function listProcesses() {
|
|
5
|
+
try {
|
|
6
|
+
return execFileSync("ps", ["-axww", "-o", "pid=,ppid=,command="], { encoding: "utf8" })
|
|
7
|
+
.split("\n")
|
|
8
|
+
.map((line) => line.match(/^\s*(\d+)\s+(\d+)\s+(.*)$/))
|
|
9
|
+
.filter((match) => match !== null)
|
|
10
|
+
.map((match) => ({ pid: Number(match[1]), ppid: Number(match[2]), command: match[3] }));
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The processes in `list` that run from `home`, apart from `self` and the processes that started
|
|
18
|
+
* it (npx, a shell).
|
|
19
|
+
*/
|
|
20
|
+
export function wizardsProcesses(home, list, self = process.pid) {
|
|
21
|
+
const parents = new Map(list.map((entry) => [entry.pid, entry.ppid]));
|
|
22
|
+
const skip = new Set();
|
|
23
|
+
for (let at = self; at && !skip.has(at); at = parents.get(at)) {
|
|
24
|
+
skip.add(at);
|
|
25
|
+
}
|
|
26
|
+
return list
|
|
27
|
+
.filter((entry) => !skip.has(entry.pid) && entry.command.includes(home + sep))
|
|
28
|
+
.map(({ pid, command }) => {
|
|
29
|
+
const args = command.split(/\s+/);
|
|
30
|
+
const kind = /apps[\\/]runtime[\\/]dist[\\/]index\.js/.test(command)
|
|
31
|
+
? "runtime"
|
|
32
|
+
: args.includes("mcp")
|
|
33
|
+
? "mcp"
|
|
34
|
+
: args.includes("start")
|
|
35
|
+
? "start"
|
|
36
|
+
: "other";
|
|
37
|
+
return { pid, kind };
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
export const findWizardsProcesses = (home) => wizardsProcesses(home, listProcesses());
|
|
41
|
+
const alive = (pid) => {
|
|
42
|
+
try {
|
|
43
|
+
process.kill(pid, 0);
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
return error.code === "EPERM";
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
/** SIGTERM, then SIGKILL after 8 seconds; the processes still there after that. */
|
|
51
|
+
export async function stopProcesses(pids) {
|
|
52
|
+
const signal = (name) => {
|
|
53
|
+
for (const pid of pids.filter(alive)) {
|
|
54
|
+
try {
|
|
55
|
+
process.kill(pid, name);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// gone meanwhile, or not ours: the check below tells
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const wait = async (ms) => {
|
|
63
|
+
for (let waited = 0; waited < ms && pids.some(alive); waited += 100) {
|
|
64
|
+
await new Promise((done) => setTimeout(done, 100));
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
signal("SIGTERM");
|
|
68
|
+
await wait(8000);
|
|
69
|
+
signal("SIGKILL");
|
|
70
|
+
await wait(2000);
|
|
71
|
+
return pids.filter(alive);
|
|
72
|
+
}
|
|
@@ -3,7 +3,7 @@ import { homedir } from "node:os";
|
|
|
3
3
|
import { delimiter, join } from "node:path";
|
|
4
4
|
import * as p from "@clack/prompts";
|
|
5
5
|
import { accountStep } from "./account.js";
|
|
6
|
-
import {
|
|
6
|
+
import { autostartForStatus, setAutostart } from "./autostart.js";
|
|
7
7
|
import { installClient, installCommand } from "./clients.js";
|
|
8
8
|
import { appStates, connectApps } from "./connect.js";
|
|
9
9
|
import { runLogged } from "./exec.js";
|
|
@@ -41,17 +41,16 @@ async function clientsStep(paths, options, interactive) {
|
|
|
41
41
|
? `Found AI clients: ${installed.map((s) => `${s.client.name} ${s.version}`).join(", ")}.`
|
|
42
42
|
: "No AI client on this computer yet.");
|
|
43
43
|
if (!installed.length) {
|
|
44
|
-
p.log.message(dim("engenty wizards
|
|
44
|
+
p.log.message(dim("engenty wizards runs its AI through an AI client, like Claude Code or Codex, on a subscription you already have."));
|
|
45
45
|
}
|
|
46
46
|
let wanted = missing.filter((s) => options.clients.includes(s.client.id));
|
|
47
|
-
|
|
47
|
+
// With a client already here, setup asks nothing more; another one is a `--client` away.
|
|
48
|
+
if (interactive && !installed.length && missing.length && !wanted.length) {
|
|
48
49
|
const suggested = suggestedClient(missing);
|
|
49
50
|
// A client whose app is on this Mac comes first: its subscription is most likely paid for.
|
|
50
51
|
const offered = [...missing].sort((a, b) => Number(!vendorApp(a.client)) - Number(!vendorApp(b.client)));
|
|
51
52
|
const answer = await p.multiselect({
|
|
52
|
-
message: installed
|
|
53
|
-
? "Install another AI client? (space to choose, enter to continue)"
|
|
54
|
-
: "Which AI client should be installed?",
|
|
53
|
+
message: "Which AI client should be installed? (space to choose, enter to continue)",
|
|
55
54
|
options: offered.map(({ client }) => {
|
|
56
55
|
const app = vendorApp(client);
|
|
57
56
|
return {
|
|
@@ -60,13 +59,13 @@ async function clientsStep(paths, options, interactive) {
|
|
|
60
59
|
hint: [
|
|
61
60
|
`${client.account} subscription`,
|
|
62
61
|
app ? `the ${app} app is on this Mac` : "",
|
|
63
|
-
|
|
62
|
+
client.id === suggested?.client.id ? "recommended" : "",
|
|
64
63
|
]
|
|
65
64
|
.filter(Boolean)
|
|
66
65
|
.join(", "),
|
|
67
66
|
};
|
|
68
67
|
}),
|
|
69
|
-
initialValues:
|
|
68
|
+
initialValues: suggested ? [suggested.client.id] : [],
|
|
70
69
|
required: false,
|
|
71
70
|
});
|
|
72
71
|
if (cancelled(answer)) {
|
|
@@ -89,7 +88,7 @@ async function clientsStep(paths, options, interactive) {
|
|
|
89
88
|
async function toolsStep(interactive) {
|
|
90
89
|
const chrome = findChrome();
|
|
91
90
|
if (chrome) {
|
|
92
|
-
p.log.success(`Browser for PDF and PNG: ${dim(tilde(chrome))}`);
|
|
91
|
+
p.log.success(`Browser for PDF and PNG exports: ${dim(tilde(chrome))}`);
|
|
93
92
|
}
|
|
94
93
|
else {
|
|
95
94
|
p.log.warn("No Chrome, Edge or Chromium found. PDF and PNG exports and browser steps need one.\nhttps://www.google.com/chrome");
|
|
@@ -129,20 +128,20 @@ async function toolsStep(interactive) {
|
|
|
129
128
|
* service does. Asked, never assumed; `autostart off` undoes it.
|
|
130
129
|
*/
|
|
131
130
|
async function autostartStep(paths, interactive) {
|
|
132
|
-
const state = await
|
|
131
|
+
const state = await autostartForStatus(paths);
|
|
133
132
|
if ("unavailable" in state) {
|
|
134
133
|
return;
|
|
135
134
|
}
|
|
136
135
|
if (state.on) {
|
|
137
|
-
p.log.success(`Starts
|
|
136
|
+
p.log.success(`Starts when you log in to this computer: ${dim(state.how)}`);
|
|
138
137
|
return;
|
|
139
138
|
}
|
|
140
139
|
if (!interactive) {
|
|
141
|
-
p.log.info(dim(`To start it when you log in: ${cmd("autostart on")}`));
|
|
140
|
+
p.log.info(dim(`To start it when you log in to this computer: ${cmd("autostart on")}`));
|
|
142
141
|
return;
|
|
143
142
|
}
|
|
144
143
|
const answer = await p.confirm({
|
|
145
|
-
message: "Start engenty wizards when you log in? It
|
|
144
|
+
message: "Start engenty wizards when you log in to this computer? It runs as a background process, without a terminal window, so links and your AI apps can always reach it.",
|
|
146
145
|
initialValue: true,
|
|
147
146
|
});
|
|
148
147
|
if (cancelled(answer)) {
|
|
@@ -155,14 +154,14 @@ async function autostartStep(paths, interactive) {
|
|
|
155
154
|
try {
|
|
156
155
|
const after = await setAutostart(paths, true);
|
|
157
156
|
if ("on" in after && after.on) {
|
|
158
|
-
p.log.success(`Starts
|
|
157
|
+
p.log.success(`Starts when you log in to this computer: ${dim(after.how)}`);
|
|
159
158
|
}
|
|
160
159
|
else {
|
|
161
|
-
p.log.warn("Couldn’t
|
|
160
|
+
p.log.warn("Couldn’t set it to start when you log in.");
|
|
162
161
|
}
|
|
163
162
|
}
|
|
164
163
|
catch (error) {
|
|
165
|
-
p.log.warn(`Couldn’t
|
|
164
|
+
p.log.warn(`Couldn’t set it to start when you log in. ${error.message}`);
|
|
166
165
|
}
|
|
167
166
|
}
|
|
168
167
|
/**
|
|
@@ -174,7 +173,7 @@ async function appsStep(paths, interactive) {
|
|
|
174
173
|
const connected = states.filter((s) => s.connected);
|
|
175
174
|
const offered = states.filter((s) => s.installed && !s.connected);
|
|
176
175
|
if (connected.length) {
|
|
177
|
-
p.log.success(`
|
|
176
|
+
p.log.success(`Already in your AI apps: ${connected.map((s) => s.name).join(", ")}`);
|
|
178
177
|
}
|
|
179
178
|
if (!offered.length) {
|
|
180
179
|
return;
|
|
@@ -184,12 +183,8 @@ async function appsStep(paths, interactive) {
|
|
|
184
183
|
return;
|
|
185
184
|
}
|
|
186
185
|
const answer = await p.multiselect({
|
|
187
|
-
message: "
|
|
188
|
-
options: offered.map((s) => ({
|
|
189
|
-
value: s.id,
|
|
190
|
-
label: s.name,
|
|
191
|
-
hint: s.widgets ? "shows runs as a widget" : "shows runs as text",
|
|
192
|
-
})),
|
|
186
|
+
message: "Add your wizards to these AI apps, as an MCP server in each app’s settings? (space to choose, enter to continue)",
|
|
187
|
+
options: offered.map((s) => ({ value: s.id, label: s.name })),
|
|
193
188
|
initialValues: offered.map((s) => s.id),
|
|
194
189
|
required: false,
|
|
195
190
|
});
|
|
@@ -261,7 +256,7 @@ async function pathStep(interactive) {
|
|
|
261
256
|
async function howToUse(paths) {
|
|
262
257
|
const [running, atLogin] = await Promise.all([
|
|
263
258
|
runningRuntime(settings(paths).dataDir),
|
|
264
|
-
|
|
259
|
+
autostartForStatus(paths),
|
|
265
260
|
]);
|
|
266
261
|
const wizards = command();
|
|
267
262
|
const row = (what, cmd, note = "") => `${what.padEnd(9)}${cyan(cmd.padEnd(wizards.length + 12))}${note ? dim(note) : ""}`;
|
|
@@ -271,7 +266,7 @@ async function howToUse(paths) {
|
|
|
271
266
|
: "engenty wizards isn’t running yet.",
|
|
272
267
|
"unavailable" in atLogin || !atLogin.on
|
|
273
268
|
? ""
|
|
274
|
-
: dim("It starts automatically when you log in."),
|
|
269
|
+
: dim("It starts automatically when you log in to this computer."),
|
|
275
270
|
]
|
|
276
271
|
.filter(Boolean)
|
|
277
272
|
.join("\n"));
|
|
@@ -322,7 +317,12 @@ export async function setup(paths, options) {
|
|
|
322
317
|
if ((await pathStep(interactive)) === "cancelled") {
|
|
323
318
|
return false;
|
|
324
319
|
}
|
|
325
|
-
writeInstallNote({
|
|
320
|
+
writeInstallNote({
|
|
321
|
+
...readInstallNote(paths),
|
|
322
|
+
setupAt: new Date().toISOString(),
|
|
323
|
+
version,
|
|
324
|
+
unattended: !process.stdout.isTTY,
|
|
325
|
+
}, paths);
|
|
326
326
|
p.log.info(CHROME_APP);
|
|
327
327
|
await howToUse(paths);
|
|
328
328
|
return true;
|
|
@@ -6,8 +6,9 @@ import { join, resolve } from "node:path";
|
|
|
6
6
|
import { mintLocalTicket } from "../auth/local-ticket.js";
|
|
7
7
|
import { readRunning } from "../running.js";
|
|
8
8
|
import { RESTART_EXIT } from "../update.js";
|
|
9
|
+
import { applyPendingUpdate, autoUpdatePolicy, notesUrl } from "./autoupdate.js";
|
|
9
10
|
import { runtimeEnv } from "./environment.js";
|
|
10
|
-
import { packageRoot, readEnvFile } from "./home.js";
|
|
11
|
+
import { installedByScript, packageRoot, readEnvFile } from "./home.js";
|
|
11
12
|
import { findOnPath } from "./machine.js";
|
|
12
13
|
import { cmd, cyan, dim, live, tilde } from "./ui.js";
|
|
13
14
|
const DEFAULT_PORT = 24368;
|
|
@@ -163,6 +164,22 @@ export async function start(paths, options) {
|
|
|
163
164
|
console.log(dim(` It was already running. To stop it: ${cmd("stop")}`));
|
|
164
165
|
return 0;
|
|
165
166
|
}
|
|
167
|
+
// Nothing runs, so no run is cut off: a version downloaded meanwhile becomes current and
|
|
168
|
+
// takes this process's place (the login item keeps waiting on the same pid). In the
|
|
169
|
+
// background, the `start` spawned below does it.
|
|
170
|
+
if (!options.background && installedByScript(paths) && process.execve) {
|
|
171
|
+
const switched = autoUpdatePolicy(paths, { installed: true }).on && applyPendingUpdate(paths);
|
|
172
|
+
if (switched) {
|
|
173
|
+
console.log(` ${live} Updated to ${switched.to}. What changed: ${notesUrl(switched.to)}`);
|
|
174
|
+
const wrapper = join(paths.bin, "wizards");
|
|
175
|
+
try {
|
|
176
|
+
process.execve(wrapper, [wrapper, "start", ...(options.open ? [] : ["--no-open"])]);
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// This version starts once more; the next start is the new one.
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
166
183
|
if (options.background) {
|
|
167
184
|
const started = await startInBackground(paths);
|
|
168
185
|
if (!started) {
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { accessSync, constants, existsSync, mkdirSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { accountState, describeAccount } from "./account.js";
|
|
4
|
-
import { autostartState, setAutostart } from "./autostart.js";
|
|
4
|
+
import { autostartForStatus, autostartState, setAutostart } from "./autostart.js";
|
|
5
|
+
import { autoUpdatePolicy, updateNotice } from "./autoupdate.js";
|
|
5
6
|
import { installedByScript, isCheckout, packageRoot, packageVersion } from "./home.js";
|
|
6
7
|
import { detectClients, findChrome, findFfmpeg, nodeIsCurrent } from "./machine.js";
|
|
8
|
+
import { findWizardsProcesses, stopProcesses } from "./processes.js";
|
|
7
9
|
import { entryUrl, runningRuntime, settings, startInBackground } from "./start.js";
|
|
8
10
|
import { bad, badge, cmd, cyan, dim, no, ok, tilde } from "./ui.js";
|
|
9
11
|
/**
|
|
@@ -16,7 +18,7 @@ export async function status(paths, checks) {
|
|
|
16
18
|
runningRuntime(dataDir),
|
|
17
19
|
detectClients(paths),
|
|
18
20
|
findFfmpeg(),
|
|
19
|
-
|
|
21
|
+
autostartForStatus(paths),
|
|
20
22
|
accountState(),
|
|
21
23
|
]);
|
|
22
24
|
const chrome = findChrome();
|
|
@@ -33,12 +35,23 @@ export async function status(paths, checks) {
|
|
|
33
35
|
if (!("unavailable" in atLogin)) {
|
|
34
36
|
row("At login", atLogin.on ? `${ok} starts ${dim(`(${atLogin.how})`)}` : `${no} doesn’t start`);
|
|
35
37
|
}
|
|
38
|
+
if (installedByScript(paths)) {
|
|
39
|
+
const policy = autoUpdatePolicy(paths, { installed: true });
|
|
40
|
+
row("Updates", policy.on ? `${ok} automatic` : `${no} ${policy.reason} ${dim(`(${cmd("autoupdate on")})`)}`);
|
|
41
|
+
}
|
|
36
42
|
row("Account", `${account.kind === "linked" ? ok : no} ${describeAccount(account)}`);
|
|
37
43
|
for (const { client, path, version } of clients) {
|
|
38
44
|
row(client.name.split(" ")[0], path ? `${ok} ${version} ${dim(tilde(path))}` : `${no} not installed`);
|
|
39
45
|
}
|
|
40
46
|
row("Browser", chrome ? `${ok} ${dim(tilde(chrome))}` : `${no} not found (needed for PDF and PNG)`);
|
|
41
47
|
row("ffmpeg", ffmpeg.path ? `${ok} ${ffmpeg.version ?? ""}` : `${no} not found (needed for MP4)`);
|
|
48
|
+
const { updated, pending } = updateNotice(paths, packageVersion());
|
|
49
|
+
if (updated) {
|
|
50
|
+
console.log(`\n ${ok} Updated to ${updated.to}. What changed: ${cyan(updated.notes)}`);
|
|
51
|
+
}
|
|
52
|
+
else if (pending) {
|
|
53
|
+
console.log(`\n ${ok} Version ${pending.version} is downloaded; it runs from the next start.`);
|
|
54
|
+
}
|
|
42
55
|
console.log("");
|
|
43
56
|
if (!checks) {
|
|
44
57
|
return 0;
|
|
@@ -84,21 +97,41 @@ async function ended(pid, ms) {
|
|
|
84
97
|
}
|
|
85
98
|
return false;
|
|
86
99
|
}
|
|
87
|
-
/**
|
|
100
|
+
/**
|
|
101
|
+
* Stops engenty wizards: the runtime that runs on this install's data folder, and every other
|
|
102
|
+
* runtime or `start` of this install still about (an older `start --no-open` whose note another
|
|
103
|
+
* one overwrote). `wizards mcp` processes belong to the AI apps and stay.
|
|
104
|
+
*/
|
|
88
105
|
export async function stop(paths) {
|
|
89
106
|
const { dataDir } = settings(paths);
|
|
90
107
|
const running = await runningRuntime(dataDir);
|
|
91
|
-
|
|
108
|
+
const found = findWizardsProcesses(paths.home);
|
|
109
|
+
const pids = [
|
|
110
|
+
...new Set([
|
|
111
|
+
...(running ? [running.pid] : []),
|
|
112
|
+
...found.filter((p) => p.kind === "runtime" || p.kind === "start").map((p) => p.pid),
|
|
113
|
+
]),
|
|
114
|
+
];
|
|
115
|
+
if (!pids.length) {
|
|
92
116
|
console.log("engenty wizards isn’t running.");
|
|
93
117
|
return 0;
|
|
94
118
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
119
|
+
const left = await stopProcesses(pids);
|
|
120
|
+
const stopped = pids.length - left.length;
|
|
121
|
+
if (stopped) {
|
|
122
|
+
console.log(pids.length === 1
|
|
123
|
+
? `Stopped (pid ${pids[0]}). Your data is kept.`
|
|
124
|
+
: `Stopped ${stopped} processes (pid ${pids.filter((pid) => !left.includes(pid)).join(", ")}). Your data is kept.`);
|
|
99
125
|
}
|
|
100
|
-
|
|
101
|
-
|
|
126
|
+
const apps = found.filter((p) => p.kind === "mcp").length;
|
|
127
|
+
if (apps && !left.length) {
|
|
128
|
+
console.log(dim(`${apps === 1 ? "An AI app connection still runs" : `${apps} AI app connections still run`}. Using a wizard there starts engenty wizards again.`));
|
|
129
|
+
}
|
|
130
|
+
if (left.length) {
|
|
131
|
+
console.error(`${left.length === 1 ? "A process" : `${left.length} processes`} of engenty wizards didn’t stop (pid ${left.join(", ")}). To stop ${left.length === 1 ? "it" : "them"}: ${cyan(`kill -9 ${left.join(" ")}`)}`);
|
|
132
|
+
return 1;
|
|
133
|
+
}
|
|
134
|
+
return 0;
|
|
102
135
|
}
|
|
103
136
|
/**
|
|
104
137
|
* Stops the runtime and starts it again, the version on disk now. One the command started (a
|
|
@@ -145,15 +178,16 @@ export async function autostart(paths, value) {
|
|
|
145
178
|
console.error(`Use ${cmd("autostart on")} or ${cmd("autostart off")}.`);
|
|
146
179
|
return 2;
|
|
147
180
|
}
|
|
148
|
-
let state
|
|
149
|
-
|
|
150
|
-
|
|
181
|
+
let state;
|
|
182
|
+
try {
|
|
183
|
+
state = await autostartState(paths);
|
|
184
|
+
if (value && !("unavailable" in state)) {
|
|
151
185
|
state = await setAutostart(paths, value === "on");
|
|
152
186
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
console.error(`Couldn’t ${value ? `turn start at login ${value}` : "check start at login"}: ${error.message}`);
|
|
190
|
+
return 1;
|
|
157
191
|
}
|
|
158
192
|
if ("unavailable" in state) {
|
|
159
193
|
console.error(`Start at login isn’t available here: ${state.unavailable}.`);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { layout } from "./home.js";
|
|
2
|
+
import { nodeIsCurrent } from "./machine.js";
|
|
3
|
+
import { parseUninstall, uninstall } from "./uninstall.js";
|
|
4
|
+
/**
|
|
5
|
+
* `wizards uninstall` on its own: bin/wizards.mjs starts it instead of main.ts, whose modules make
|
|
6
|
+
* the install folder, its data folder and a secret when they load (install-env.ts, env.ts,
|
|
7
|
+
* db/client.ts). An uninstall must find the computer as it is, and leave nothing behind.
|
|
8
|
+
*/
|
|
9
|
+
if (!nodeIsCurrent()) {
|
|
10
|
+
console.error(`engenty wizards needs Node.js 24.11 or newer. This is ${process.version}.`);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
const options = parseUninstall(process.argv.slice(2));
|
|
14
|
+
if (typeof options === "string") {
|
|
15
|
+
console.error(options);
|
|
16
|
+
process.exitCode = 2;
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
process.exitCode = await uninstall(layout(), options);
|
|
20
|
+
}
|