trooper-cli 0.1.0 → 0.1.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/bin/trooper.js +95 -7
- package/package.json +1 -1
package/bin/trooper.js
CHANGED
|
@@ -5,6 +5,12 @@ import { randomBytes } from 'node:crypto';
|
|
|
5
5
|
|
|
6
6
|
const DEFAULT_API_URL = 'https://trooper-production.up.railway.app';
|
|
7
7
|
const LOCAL_MAC_SETUP_SCRIPT_URL = 'https://raw.githubusercontent.com/absurdfounder/trooper-bridge/main/setup-local-mac-host.sh';
|
|
8
|
+
const LOCAL_MAC_LABELS = [
|
|
9
|
+
'so.trooper.local-gateway',
|
|
10
|
+
'so.trooper.local-bridge',
|
|
11
|
+
'so.trooper.local-tunnel',
|
|
12
|
+
'so.trooper.local-heartbeat',
|
|
13
|
+
];
|
|
8
14
|
|
|
9
15
|
function printHelp() {
|
|
10
16
|
console.log(`Trooper CLI
|
|
@@ -12,15 +18,19 @@ function printHelp() {
|
|
|
12
18
|
Usage:
|
|
13
19
|
npx -y trooper-cli onboard --yes
|
|
14
20
|
npx -y trooper-cli onboard --yes --token <setup-token>
|
|
21
|
+
npx -y trooper-cli uninstall --yes
|
|
15
22
|
|
|
16
23
|
Aliases:
|
|
17
24
|
setup Same as onboard
|
|
25
|
+
remove Same as uninstall
|
|
18
26
|
|
|
19
27
|
Options:
|
|
20
28
|
--token <token> Optional short-lived setup token from Trooper for workspace pairing
|
|
21
29
|
--api <url> Trooper API URL (defaults to production)
|
|
22
30
|
--platform <name> macos, windows, or linux (defaults to this computer)
|
|
23
31
|
--yes Run non-interactively with sensible defaults
|
|
32
|
+
--keep-data Preserve local workspace/config data during uninstall
|
|
33
|
+
--remove-app Also remove Trooper.app when uninstalling, if it is writable
|
|
24
34
|
--print-command Print the installer command without running it
|
|
25
35
|
--dry-run Same as --print-command
|
|
26
36
|
-h, --help Show this help
|
|
@@ -37,7 +47,7 @@ function parseArgs(argv) {
|
|
|
37
47
|
}
|
|
38
48
|
const [key, inlineValue] = arg.split('=', 2);
|
|
39
49
|
const name = key.replace(/^-+/, '');
|
|
40
|
-
if (['help', 'h', 'dry-run', 'print-command', 'yes', 'y'].includes(name)) {
|
|
50
|
+
if (['help', 'h', 'dry-run', 'print-command', 'yes', 'y', 'keep-data', 'remove-app'].includes(name)) {
|
|
41
51
|
args[name] = true;
|
|
42
52
|
continue;
|
|
43
53
|
}
|
|
@@ -96,6 +106,62 @@ function buildLocalMacInstallCommand({ apiUrl }) {
|
|
|
96
106
|
].join('\n');
|
|
97
107
|
}
|
|
98
108
|
|
|
109
|
+
function buildLocalMacUninstallCommand({ keepData = false, removeApp = false } = {}) {
|
|
110
|
+
const labels = LOCAL_MAC_LABELS.map(shellSingleQuote).join(' ');
|
|
111
|
+
return `set -euo pipefail
|
|
112
|
+
TROOPER_HOME="\${TROOPER_HOME:-$HOME/Library/Application Support/Trooper/runtime}"
|
|
113
|
+
TROOPER_PARENT_DIR="\$(dirname "$TROOPER_HOME")"
|
|
114
|
+
PLIST_DIR="$HOME/Library/LaunchAgents"
|
|
115
|
+
OPENCLAW_GATEWAY_CONTAINER="\${OPENCLAW_GATEWAY_CONTAINER:-openclaw-openclaw-gateway-1}"
|
|
116
|
+
LABELS=(${labels})
|
|
117
|
+
|
|
118
|
+
echo "Uninstalling Trooper local host from this Mac..."
|
|
119
|
+
|
|
120
|
+
ROOT_OWNED_PATHS=()
|
|
121
|
+
for path in "$TROOPER_HOME" "$TROOPER_PARENT_DIR/install-local-host.command" "$PLIST_DIR"/so.trooper.local-*.plist; do
|
|
122
|
+
if [[ -e "$path" && ! -O "$path" ]]; then
|
|
123
|
+
ROOT_OWNED_PATHS+=("$path")
|
|
124
|
+
fi
|
|
125
|
+
done
|
|
126
|
+
if (( \${#ROOT_OWNED_PATHS[@]} > 0 )); then
|
|
127
|
+
echo "Repairing ownership from an earlier Trooper local-host installation..."
|
|
128
|
+
sudo chown -R "\$(id -u):\$(id -g)" "\${ROOT_OWNED_PATHS[@]}"
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
for label in "\${LABELS[@]}"; do
|
|
132
|
+
launchctl bootout "gui/\$(id -u)/$label" >/dev/null 2>&1 || true
|
|
133
|
+
launchctl bootout "gui/\$(id -u)" "$PLIST_DIR/$label.plist" >/dev/null 2>&1 || true
|
|
134
|
+
rm -f "$PLIST_DIR/$label.plist"
|
|
135
|
+
done
|
|
136
|
+
|
|
137
|
+
if command -v docker >/dev/null 2>&1; then
|
|
138
|
+
docker rm -f "$OPENCLAW_GATEWAY_CONTAINER" trooper-local-gateway >/dev/null 2>&1 || true
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
rm -f "$TROOPER_PARENT_DIR/install-local-host.command" /tmp/trooper-local-mac-host.sh
|
|
142
|
+
|
|
143
|
+
if [[ ${keepData ? '1' : '0'} == "1" ]]; then
|
|
144
|
+
rm -rf "$TROOPER_HOME/bridge" "$TROOPER_HOME/bin" "$TROOPER_HOME/logs"
|
|
145
|
+
rm -f "$TROOPER_HOME/trooper-local-host.env"
|
|
146
|
+
echo "Preserved local Trooper data at: $TROOPER_HOME/openclaw-data"
|
|
147
|
+
else
|
|
148
|
+
rm -rf "$TROOPER_HOME"
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
if [[ ${removeApp ? '1' : '0'} == "1" ]]; then
|
|
152
|
+
rm -rf "$HOME/Applications/Trooper.app" 2>/dev/null || true
|
|
153
|
+
if [[ -d "/Applications/Trooper.app" ]]; then
|
|
154
|
+
rm -rf "/Applications/Trooper.app" 2>/dev/null || echo "Trooper.app in /Applications still exists; remove it from Finder or rerun with permission."
|
|
155
|
+
fi
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
echo "Trooper local host has been uninstalled."
|
|
159
|
+
if [[ ${keepData ? '1' : '0'} != "1" ]]; then
|
|
160
|
+
echo "Local runtime data was removed from: $TROOPER_HOME"
|
|
161
|
+
fi
|
|
162
|
+
echo "Docker, Colima, Homebrew, Node, and npm were left installed."`;
|
|
163
|
+
}
|
|
164
|
+
|
|
99
165
|
async function fetchSetupGuide({ apiUrl, token, platform }) {
|
|
100
166
|
const res = await fetch(`${apiUrl}/api/local-host/setup`, {
|
|
101
167
|
method: 'POST',
|
|
@@ -118,19 +184,19 @@ async function fetchSetupGuide({ apiUrl, token, platform }) {
|
|
|
118
184
|
return data;
|
|
119
185
|
}
|
|
120
186
|
|
|
121
|
-
function
|
|
187
|
+
function runShellCommand(command, platform, { failureLabel = 'Command', successMessage = '' } = {}) {
|
|
122
188
|
const isWindows = platform === 'windows' || process.platform === 'win32';
|
|
123
189
|
const child = isWindows
|
|
124
190
|
? spawn('powershell.exe', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', command], { stdio: 'inherit' })
|
|
125
191
|
: spawn(process.platform === 'darwin' ? '/bin/zsh' : '/bin/bash', ['-lc', command], { stdio: 'inherit' });
|
|
126
192
|
|
|
127
193
|
child.on('error', (error) => {
|
|
128
|
-
console.error(`Could not start
|
|
194
|
+
console.error(`Could not start ${failureLabel.toLowerCase()}: ${error.message}`);
|
|
129
195
|
process.exit(1);
|
|
130
196
|
});
|
|
131
197
|
child.on('exit', (code, signal) => {
|
|
132
198
|
if (signal) {
|
|
133
|
-
console.error(
|
|
199
|
+
console.error(`${failureLabel} stopped by signal ${signal}`);
|
|
134
200
|
process.exit(1);
|
|
135
201
|
}
|
|
136
202
|
if ((code ?? 0) === 0 && successMessage) {
|
|
@@ -147,7 +213,7 @@ async function main() {
|
|
|
147
213
|
printHelp();
|
|
148
214
|
return;
|
|
149
215
|
}
|
|
150
|
-
if (!['setup', 'onboard'].includes(command)) {
|
|
216
|
+
if (!['setup', 'onboard', 'uninstall', 'remove'].includes(command)) {
|
|
151
217
|
console.error(`Unknown command: ${command}`);
|
|
152
218
|
printHelp();
|
|
153
219
|
process.exit(1);
|
|
@@ -157,6 +223,27 @@ async function main() {
|
|
|
157
223
|
const platform = String(args.platform || process.env.TROOPER_SETUP_PLATFORM || detectPlatform()).trim().toLowerCase();
|
|
158
224
|
const token = String(args.token || args['setup-token'] || process.env.TROOPER_SETUP_TOKEN || '').trim();
|
|
159
225
|
|
|
226
|
+
if (command === 'uninstall' || command === 'remove') {
|
|
227
|
+
if (platform !== 'macos') {
|
|
228
|
+
console.error('Trooper local-host uninstall is currently available for macOS.');
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
231
|
+
if (!args.yes && !args.y && !args['print-command'] && !args['dry-run']) {
|
|
232
|
+
console.error('Refusing to uninstall without --yes. Run: npx -y trooper-cli uninstall --yes');
|
|
233
|
+
process.exit(1);
|
|
234
|
+
}
|
|
235
|
+
const uninstallCommand = buildLocalMacUninstallCommand({
|
|
236
|
+
keepData: Boolean(args['keep-data']),
|
|
237
|
+
removeApp: Boolean(args['remove-app']),
|
|
238
|
+
});
|
|
239
|
+
if (args['print-command'] || args['dry-run']) {
|
|
240
|
+
console.log(uninstallCommand);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
runShellCommand(uninstallCommand, platform, { failureLabel: 'Uninstall' });
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
160
247
|
if (!token) {
|
|
161
248
|
if (platform !== 'macos') {
|
|
162
249
|
console.error('Tokenless local install is currently available for macOS. Open Trooper to prepare a paired installer for this platform.');
|
|
@@ -168,7 +255,8 @@ async function main() {
|
|
|
168
255
|
console.log('\n' + installCommand);
|
|
169
256
|
return;
|
|
170
257
|
}
|
|
171
|
-
|
|
258
|
+
runShellCommand(installCommand, platform, {
|
|
259
|
+
failureLabel: 'Installer',
|
|
172
260
|
successMessage: '\nTrooper local host is installed. Open Trooper to connect this Mac to your workspace.',
|
|
173
261
|
});
|
|
174
262
|
return;
|
|
@@ -184,7 +272,7 @@ async function main() {
|
|
|
184
272
|
return;
|
|
185
273
|
}
|
|
186
274
|
|
|
187
|
-
|
|
275
|
+
runShellCommand(guide.installCommand, platform, { failureLabel: 'Installer' });
|
|
188
276
|
}
|
|
189
277
|
|
|
190
278
|
main().catch((error) => {
|