trooper-cli 0.1.0 → 0.1.2
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 +96 -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,63 @@ 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
|
+
export PATH="/opt/homebrew/bin:/usr/local/bin:/Applications/Docker.app/Contents/Resources/bin:$HOME/Applications/Docker.app/Contents/Resources/bin:/usr/bin:/bin:/usr/sbin:/sbin"
|
|
113
|
+
TROOPER_HOME="\${TROOPER_HOME:-$HOME/Library/Application Support/Trooper/runtime}"
|
|
114
|
+
TROOPER_PARENT_DIR="\$(dirname "$TROOPER_HOME")"
|
|
115
|
+
PLIST_DIR="$HOME/Library/LaunchAgents"
|
|
116
|
+
OPENCLAW_GATEWAY_CONTAINER="\${OPENCLAW_GATEWAY_CONTAINER:-openclaw-openclaw-gateway-1}"
|
|
117
|
+
LABELS=(${labels})
|
|
118
|
+
|
|
119
|
+
echo "Uninstalling Trooper local host from this Mac..."
|
|
120
|
+
|
|
121
|
+
ROOT_OWNED_PATHS=()
|
|
122
|
+
for path in "$TROOPER_HOME" "$TROOPER_PARENT_DIR/install-local-host.command" "$PLIST_DIR"/so.trooper.local-*.plist; do
|
|
123
|
+
if [[ -e "$path" && ! -O "$path" ]]; then
|
|
124
|
+
ROOT_OWNED_PATHS+=("$path")
|
|
125
|
+
fi
|
|
126
|
+
done
|
|
127
|
+
if (( \${#ROOT_OWNED_PATHS[@]} > 0 )); then
|
|
128
|
+
echo "Repairing ownership from an earlier Trooper local-host installation..."
|
|
129
|
+
sudo chown -R "\$(id -u):\$(id -g)" "\${ROOT_OWNED_PATHS[@]}"
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
for label in "\${LABELS[@]}"; do
|
|
133
|
+
launchctl bootout "gui/\$(id -u)/$label" >/dev/null 2>&1 || true
|
|
134
|
+
launchctl bootout "gui/\$(id -u)" "$PLIST_DIR/$label.plist" >/dev/null 2>&1 || true
|
|
135
|
+
rm -f "$PLIST_DIR/$label.plist"
|
|
136
|
+
done
|
|
137
|
+
|
|
138
|
+
if command -v docker >/dev/null 2>&1; then
|
|
139
|
+
docker rm -f "$OPENCLAW_GATEWAY_CONTAINER" trooper-local-gateway >/dev/null 2>&1 || true
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
rm -f "$TROOPER_PARENT_DIR/install-local-host.command" /tmp/trooper-local-mac-host.sh
|
|
143
|
+
|
|
144
|
+
if [[ ${keepData ? '1' : '0'} == "1" ]]; then
|
|
145
|
+
rm -rf "$TROOPER_HOME/bridge" "$TROOPER_HOME/bin" "$TROOPER_HOME/logs"
|
|
146
|
+
rm -f "$TROOPER_HOME/trooper-local-host.env"
|
|
147
|
+
echo "Preserved local Trooper data at: $TROOPER_HOME/openclaw-data"
|
|
148
|
+
else
|
|
149
|
+
rm -rf "$TROOPER_HOME"
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
if [[ ${removeApp ? '1' : '0'} == "1" ]]; then
|
|
153
|
+
rm -rf "$HOME/Applications/Trooper.app" 2>/dev/null || true
|
|
154
|
+
if [[ -d "/Applications/Trooper.app" ]]; then
|
|
155
|
+
rm -rf "/Applications/Trooper.app" 2>/dev/null || echo "Trooper.app in /Applications still exists; remove it from Finder or rerun with permission."
|
|
156
|
+
fi
|
|
157
|
+
fi
|
|
158
|
+
|
|
159
|
+
echo "Trooper local host has been uninstalled."
|
|
160
|
+
if [[ ${keepData ? '1' : '0'} != "1" ]]; then
|
|
161
|
+
echo "Local runtime data was removed from: $TROOPER_HOME"
|
|
162
|
+
fi
|
|
163
|
+
echo "Docker, Colima, Homebrew, Node, and npm were left installed."`;
|
|
164
|
+
}
|
|
165
|
+
|
|
99
166
|
async function fetchSetupGuide({ apiUrl, token, platform }) {
|
|
100
167
|
const res = await fetch(`${apiUrl}/api/local-host/setup`, {
|
|
101
168
|
method: 'POST',
|
|
@@ -118,19 +185,19 @@ async function fetchSetupGuide({ apiUrl, token, platform }) {
|
|
|
118
185
|
return data;
|
|
119
186
|
}
|
|
120
187
|
|
|
121
|
-
function
|
|
188
|
+
function runShellCommand(command, platform, { failureLabel = 'Command', successMessage = '' } = {}) {
|
|
122
189
|
const isWindows = platform === 'windows' || process.platform === 'win32';
|
|
123
190
|
const child = isWindows
|
|
124
191
|
? spawn('powershell.exe', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', command], { stdio: 'inherit' })
|
|
125
192
|
: spawn(process.platform === 'darwin' ? '/bin/zsh' : '/bin/bash', ['-lc', command], { stdio: 'inherit' });
|
|
126
193
|
|
|
127
194
|
child.on('error', (error) => {
|
|
128
|
-
console.error(`Could not start
|
|
195
|
+
console.error(`Could not start ${failureLabel.toLowerCase()}: ${error.message}`);
|
|
129
196
|
process.exit(1);
|
|
130
197
|
});
|
|
131
198
|
child.on('exit', (code, signal) => {
|
|
132
199
|
if (signal) {
|
|
133
|
-
console.error(
|
|
200
|
+
console.error(`${failureLabel} stopped by signal ${signal}`);
|
|
134
201
|
process.exit(1);
|
|
135
202
|
}
|
|
136
203
|
if ((code ?? 0) === 0 && successMessage) {
|
|
@@ -147,7 +214,7 @@ async function main() {
|
|
|
147
214
|
printHelp();
|
|
148
215
|
return;
|
|
149
216
|
}
|
|
150
|
-
if (!['setup', 'onboard'].includes(command)) {
|
|
217
|
+
if (!['setup', 'onboard', 'uninstall', 'remove'].includes(command)) {
|
|
151
218
|
console.error(`Unknown command: ${command}`);
|
|
152
219
|
printHelp();
|
|
153
220
|
process.exit(1);
|
|
@@ -157,6 +224,27 @@ async function main() {
|
|
|
157
224
|
const platform = String(args.platform || process.env.TROOPER_SETUP_PLATFORM || detectPlatform()).trim().toLowerCase();
|
|
158
225
|
const token = String(args.token || args['setup-token'] || process.env.TROOPER_SETUP_TOKEN || '').trim();
|
|
159
226
|
|
|
227
|
+
if (command === 'uninstall' || command === 'remove') {
|
|
228
|
+
if (platform !== 'macos') {
|
|
229
|
+
console.error('Trooper local-host uninstall is currently available for macOS.');
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
if (!args.yes && !args.y && !args['print-command'] && !args['dry-run']) {
|
|
233
|
+
console.error('Refusing to uninstall without --yes. Run: npx -y trooper-cli uninstall --yes');
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
const uninstallCommand = buildLocalMacUninstallCommand({
|
|
237
|
+
keepData: Boolean(args['keep-data']),
|
|
238
|
+
removeApp: Boolean(args['remove-app']),
|
|
239
|
+
});
|
|
240
|
+
if (args['print-command'] || args['dry-run']) {
|
|
241
|
+
console.log(uninstallCommand);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
runShellCommand(uninstallCommand, platform, { failureLabel: 'Uninstall' });
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
|
|
160
248
|
if (!token) {
|
|
161
249
|
if (platform !== 'macos') {
|
|
162
250
|
console.error('Tokenless local install is currently available for macOS. Open Trooper to prepare a paired installer for this platform.');
|
|
@@ -168,7 +256,8 @@ async function main() {
|
|
|
168
256
|
console.log('\n' + installCommand);
|
|
169
257
|
return;
|
|
170
258
|
}
|
|
171
|
-
|
|
259
|
+
runShellCommand(installCommand, platform, {
|
|
260
|
+
failureLabel: 'Installer',
|
|
172
261
|
successMessage: '\nTrooper local host is installed. Open Trooper to connect this Mac to your workspace.',
|
|
173
262
|
});
|
|
174
263
|
return;
|
|
@@ -184,7 +273,7 @@ async function main() {
|
|
|
184
273
|
return;
|
|
185
274
|
}
|
|
186
275
|
|
|
187
|
-
|
|
276
|
+
runShellCommand(guide.installCommand, platform, { failureLabel: 'Installer' });
|
|
188
277
|
}
|
|
189
278
|
|
|
190
279
|
main().catch((error) => {
|