@rubytech/taskmaster 1.0.30 → 1.0.31
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/dist/build-info.json +3 -3
- package/dist/cli/program/register.maintenance.js +3 -1
- package/dist/commands/uninstall.js +77 -8
- package/dist/control-ui/assets/{index-BZ2Jt_PC.js → index-BTi4rBlY.js} +211 -154
- package/dist/control-ui/assets/index-BTi4rBlY.js.map +1 -0
- package/dist/control-ui/assets/uninstall-mUk7IPTt.js +2 -0
- package/dist/control-ui/assets/uninstall-mUk7IPTt.js.map +1 -0
- package/dist/control-ui/index.html +1 -1
- package/dist/gateway/server-methods/system.js +33 -0
- package/dist/gateway/server-methods.js +2 -1
- package/package.json +1 -1
- package/taskmaster-docs/USER-GUIDE.md +35 -0
- package/dist/control-ui/assets/index-BZ2Jt_PC.js.map +0 -1
package/dist/build-info.json
CHANGED
|
@@ -64,13 +64,14 @@ export function registerMaintenanceCommands(program) {
|
|
|
64
64
|
});
|
|
65
65
|
program
|
|
66
66
|
.command("uninstall")
|
|
67
|
-
.description("Uninstall the gateway service + local data (CLI remains)")
|
|
67
|
+
.description("Uninstall the gateway service + local data (CLI remains unless --purge)")
|
|
68
68
|
.addHelpText("after", () => `\n${theme.muted("Docs:")} ${formatDocsLink("/cli/uninstall", "docs.taskmaster.bot/cli/uninstall")}\n`)
|
|
69
69
|
.option("--service", "Remove the gateway service", false)
|
|
70
70
|
.option("--state", "Remove state + config", false)
|
|
71
71
|
.option("--workspace", "Remove workspace dirs", false)
|
|
72
72
|
.option("--app", "Remove the macOS app", false)
|
|
73
73
|
.option("--all", "Remove service + state + workspace + app", false)
|
|
74
|
+
.option("--purge", "Remove everything including the npm package itself", false)
|
|
74
75
|
.option("--yes", "Skip confirmation prompts", false)
|
|
75
76
|
.option("--non-interactive", "Disable prompts (requires --yes)", false)
|
|
76
77
|
.option("--dry-run", "Print actions without removing files", false)
|
|
@@ -82,6 +83,7 @@ export function registerMaintenanceCommands(program) {
|
|
|
82
83
|
workspace: Boolean(opts.workspace),
|
|
83
84
|
app: Boolean(opts.app),
|
|
84
85
|
all: Boolean(opts.all),
|
|
86
|
+
purge: Boolean(opts.purge),
|
|
85
87
|
yes: Boolean(opts.yes),
|
|
86
88
|
nonInteractive: Boolean(opts.nonInteractive),
|
|
87
89
|
dryRun: Boolean(opts.dryRun),
|
|
@@ -1,27 +1,31 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
1
3
|
import path from "node:path";
|
|
4
|
+
import { promisify } from "node:util";
|
|
2
5
|
import { cancel, confirm, isCancel, multiselect } from "@clack/prompts";
|
|
3
6
|
import { isNixMode, loadConfig, resolveConfigPath, resolveOAuthDir, resolveStateDir, } from "../config/config.js";
|
|
4
7
|
import { resolveGatewayService } from "../daemon/service.js";
|
|
5
8
|
import { stylePromptHint, stylePromptMessage, stylePromptTitle } from "../terminal/prompt-style.js";
|
|
6
9
|
import { resolveHomeDir } from "../utils.js";
|
|
7
10
|
import { collectWorkspaceDirs, isPathWithin, removePath } from "./cleanup-utils.js";
|
|
11
|
+
const execFileAsync = promisify(execFile);
|
|
8
12
|
const multiselectStyled = (params) => multiselect({
|
|
9
13
|
...params,
|
|
10
14
|
message: stylePromptMessage(params.message),
|
|
11
15
|
options: params.options.map((opt) => opt.hint === undefined ? opt : { ...opt, hint: stylePromptHint(opt.hint) }),
|
|
12
16
|
});
|
|
13
17
|
function buildScopeSelection(opts) {
|
|
14
|
-
const hadExplicit = Boolean(opts.all || opts.service || opts.state || opts.workspace || opts.app);
|
|
18
|
+
const hadExplicit = Boolean(opts.all || opts.purge || opts.service || opts.state || opts.workspace || opts.app);
|
|
15
19
|
const scopes = new Set();
|
|
16
|
-
if (opts.all || opts.service)
|
|
20
|
+
if (opts.all || opts.purge || opts.service)
|
|
17
21
|
scopes.add("service");
|
|
18
|
-
if (opts.all || opts.state)
|
|
22
|
+
if (opts.all || opts.purge || opts.state)
|
|
19
23
|
scopes.add("state");
|
|
20
|
-
if (opts.all || opts.workspace)
|
|
24
|
+
if (opts.all || opts.purge || opts.workspace)
|
|
21
25
|
scopes.add("workspace");
|
|
22
26
|
if (opts.all || opts.app)
|
|
23
27
|
scopes.add("app");
|
|
24
|
-
return { scopes, hadExplicit };
|
|
28
|
+
return { scopes, hadExplicit, purge: Boolean(opts.purge) };
|
|
25
29
|
}
|
|
26
30
|
async function stopAndUninstallService(runtime) {
|
|
27
31
|
if (isNixMode) {
|
|
@@ -64,8 +68,66 @@ async function removeMacApp(runtime, dryRun) {
|
|
|
64
68
|
label: "/Applications/Taskmaster.app",
|
|
65
69
|
});
|
|
66
70
|
}
|
|
71
|
+
async function removeLegacyAvahiService(runtime, dryRun) {
|
|
72
|
+
if (process.platform !== "linux")
|
|
73
|
+
return;
|
|
74
|
+
const avahiPath = "/etc/avahi/services/taskmaster.service";
|
|
75
|
+
try {
|
|
76
|
+
await fs.access(avahiPath);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return; // doesn't exist
|
|
80
|
+
}
|
|
81
|
+
if (dryRun) {
|
|
82
|
+
runtime.log(`[dry-run] remove ${avahiPath}`);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
await fs.rm(avahiPath, { force: true });
|
|
87
|
+
runtime.log(`Removed ${avahiPath}`);
|
|
88
|
+
try {
|
|
89
|
+
await execFileAsync("systemctl", ["restart", "avahi-daemon"]);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// avahi restart is best-effort
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
runtime.log(`Could not remove ${avahiPath} (may need sudo).`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async function removeRootStateDirs(runtime, dryRun) {
|
|
100
|
+
if (process.platform !== "linux")
|
|
101
|
+
return;
|
|
102
|
+
const dirs = ["/root/.taskmaster", "/root/taskmaster"];
|
|
103
|
+
for (const dir of dirs) {
|
|
104
|
+
try {
|
|
105
|
+
await fs.access(dir);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
continue; // doesn't exist
|
|
109
|
+
}
|
|
110
|
+
await removePath(dir, runtime, { dryRun, label: dir });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async function removeNpmPackage(runtime, dryRun) {
|
|
114
|
+
if (dryRun) {
|
|
115
|
+
runtime.log("[dry-run] npm uninstall -g @rubytech/taskmaster");
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
await execFileAsync("npm", ["uninstall", "-g", "@rubytech/taskmaster"], {
|
|
120
|
+
timeout: 30_000,
|
|
121
|
+
});
|
|
122
|
+
runtime.log("Removed @rubytech/taskmaster global package.");
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
runtime.log("Could not remove the npm package (likely needs elevated permissions).\n" +
|
|
126
|
+
"Run manually: sudo npm uninstall -g @rubytech/taskmaster");
|
|
127
|
+
}
|
|
128
|
+
}
|
|
67
129
|
export async function uninstallCommand(runtime, opts) {
|
|
68
|
-
const { scopes, hadExplicit } = buildScopeSelection(opts);
|
|
130
|
+
const { scopes, hadExplicit, purge } = buildScopeSelection(opts);
|
|
69
131
|
const interactive = !opts.nonInteractive;
|
|
70
132
|
if (!interactive && !opts.yes) {
|
|
71
133
|
runtime.error("Non-interactive mode requires --yes.");
|
|
@@ -151,8 +213,15 @@ export async function uninstallCommand(runtime, opts) {
|
|
|
151
213
|
if (scopes.has("app")) {
|
|
152
214
|
await removeMacApp(runtime, dryRun);
|
|
153
215
|
}
|
|
154
|
-
|
|
155
|
-
|
|
216
|
+
if (purge) {
|
|
217
|
+
await removeLegacyAvahiService(runtime, dryRun);
|
|
218
|
+
await removeRootStateDirs(runtime, dryRun);
|
|
219
|
+
await removeNpmPackage(runtime, dryRun);
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
runtime.log("CLI still installed. Remove via npm/pnpm if desired.");
|
|
223
|
+
}
|
|
224
|
+
if (!purge && scopes.has("state") && !scopes.has("workspace")) {
|
|
156
225
|
const home = resolveHomeDir();
|
|
157
226
|
if (home && workspaceDirs.some((dir) => dir.startsWith(path.resolve(home)))) {
|
|
158
227
|
runtime.log("Tip: workspaces were preserved. Re-run with --workspace to remove them.");
|