@wangyaoshen/remux 0.3.8-dev.29e114b
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/.github/ISSUE_TEMPLATE/bug_report.md +47 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +38 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +28 -0
- package/.github/dependabot.yml +33 -0
- package/.github/workflows/ci.yml +65 -0
- package/.github/workflows/deploy.yml +65 -0
- package/.github/workflows/publish.yml +312 -0
- package/.github/workflows/release-please.yml +21 -0
- package/.gitmodules +3 -0
- package/.nvmrc +1 -0
- package/.release-please-manifest.json +3 -0
- package/CLAUDE.md +104 -0
- package/Dockerfile +23 -0
- package/LICENSE +21 -0
- package/README.md +120 -0
- package/apps/ios/Config/signing.xcconfig +4 -0
- package/apps/ios/Package.swift +26 -0
- package/apps/ios/Remux.xcodeproj/project.pbxproj +477 -0
- package/apps/ios/Remux.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/Contents.json +23 -0
- package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_1024x1024.png +0 -0
- package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_120x120.png +0 -0
- package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_152x152.png +0 -0
- package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_167x167.png +0 -0
- package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_180x180.png +0 -0
- package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_20x20.png +0 -0
- package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_29x29.png +0 -0
- package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_40x40.png +0 -0
- package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_58x58.png +0 -0
- package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_60x60.png +0 -0
- package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_76x76.png +0 -0
- package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_80x80.png +0 -0
- package/apps/ios/Sources/Remux/Assets.xcassets/AppIcon.appiconset/icon_87x87.png +0 -0
- package/apps/ios/Sources/Remux/Assets.xcassets/Contents.json +6 -0
- package/apps/ios/Sources/Remux/Extensions/FaceIDManager.swift +29 -0
- package/apps/ios/Sources/Remux/Extensions/InspectCache.swift +66 -0
- package/apps/ios/Sources/Remux/MainTabView.swift +32 -0
- package/apps/ios/Sources/Remux/Remux.entitlements +8 -0
- package/apps/ios/Sources/Remux/RemuxiOSApp.swift +14 -0
- package/apps/ios/Sources/Remux/RootView.swift +130 -0
- package/apps/ios/Sources/Remux/Views/Control/ControlView.swift +102 -0
- package/apps/ios/Sources/Remux/Views/Inspect/InspectView.swift +98 -0
- package/apps/ios/Sources/Remux/Views/Live/LiveTerminalView.swift +132 -0
- package/apps/ios/Sources/Remux/Views/Now/NowView.swift +173 -0
- package/apps/ios/Sources/Remux/Views/Onboarding/ManualConnectView.swift +55 -0
- package/apps/ios/Sources/Remux/Views/Onboarding/OnboardingView.swift +70 -0
- package/apps/ios/Sources/Remux/Views/Onboarding/QRScannerView.swift +92 -0
- package/apps/ios/Sources/Remux/Views/Settings/MeView.swift +136 -0
- package/apps/macos/Package.swift +37 -0
- package/apps/macos/Resources/shell-integration/bash/bash-preexec.sh +382 -0
- package/apps/macos/Resources/shell-integration/bash/ghostty.bash +315 -0
- package/apps/macos/Resources/shell-integration/elvish/lib/ghostty-integration.elv +191 -0
- package/apps/macos/Resources/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish +246 -0
- package/apps/macos/Resources/shell-integration/nushell/vendor/autoload/ghostty.nu +110 -0
- package/apps/macos/Resources/shell-integration/zsh/.zshenv +61 -0
- package/apps/macos/Resources/shell-integration/zsh/ghostty-integration +458 -0
- package/apps/macos/Resources/terminfo/67/ghostty +0 -0
- package/apps/macos/Resources/terminfo/78/xterm-ghostty +0 -0
- package/apps/macos/Sources/Remux/AppDelegate.swift +257 -0
- package/apps/macos/Sources/Remux/CrashReporter.swift +210 -0
- package/apps/macos/Sources/Remux/FinderIntegration.swift +117 -0
- package/apps/macos/Sources/Remux/GhosttyConfig.swift +311 -0
- package/apps/macos/Sources/Remux/KeyboardShortcuts/ShortcutAction.swift +115 -0
- package/apps/macos/Sources/Remux/KeyboardShortcuts/ShortcutSettingsView.swift +271 -0
- package/apps/macos/Sources/Remux/KeyboardShortcuts/StoredShortcut.swift +149 -0
- package/apps/macos/Sources/Remux/MainContentView.swift +308 -0
- package/apps/macos/Sources/Remux/MenuBarManager.swift +275 -0
- package/apps/macos/Sources/Remux/NotificationManager.swift +145 -0
- package/apps/macos/Sources/Remux/PortScanner.swift +152 -0
- package/apps/macos/Sources/Remux/RemuxApp.swift +13 -0
- package/apps/macos/Sources/Remux/SSHDetector.swift +151 -0
- package/apps/macos/Sources/Remux/SessionPersistence.swift +226 -0
- package/apps/macos/Sources/Remux/SocketController.swift +258 -0
- package/apps/macos/Sources/Remux/UpdateChecker.swift +152 -0
- package/apps/macos/Sources/Remux/Views/CommandPalette.swift +198 -0
- package/apps/macos/Sources/Remux/Views/ConnectionView.swift +84 -0
- package/apps/macos/Sources/Remux/Views/InspectView.swift +127 -0
- package/apps/macos/Sources/Remux/Views/SettingsView.swift +77 -0
- package/apps/macos/Sources/Remux/Views/Sidebar/SidebarView.swift +410 -0
- package/apps/macos/Sources/Remux/Views/SplitTree/BrowserPanel.swift +193 -0
- package/apps/macos/Sources/Remux/Views/SplitTree/MarkdownPanel.swift +277 -0
- package/apps/macos/Sources/Remux/Views/SplitTree/PanelProtocol.swift +14 -0
- package/apps/macos/Sources/Remux/Views/SplitTree/SplitNode.swift +149 -0
- package/apps/macos/Sources/Remux/Views/SplitTree/SplitView.swift +234 -0
- package/apps/macos/Sources/Remux/Views/SplitTree/TerminalPanel.swift +26 -0
- package/apps/macos/Sources/Remux/Views/TabBarView.swift +94 -0
- package/apps/macos/Sources/Remux/Views/Terminal/ClipboardHelper.swift +101 -0
- package/apps/macos/Sources/Remux/Views/Terminal/CopyModeOverlay.swift +325 -0
- package/apps/macos/Sources/Remux/Views/Terminal/GhosttyNativeTerminalView.swift +39 -0
- package/apps/macos/Sources/Remux/Views/Terminal/GhosttyNativeView.swift +559 -0
- package/apps/macos/Sources/Remux/Views/Terminal/SurfaceSearchOverlay.swift +109 -0
- package/apps/macos/Sources/Remux/Views/Terminal/TerminalContainerView.swift +95 -0
- package/apps/macos/Sources/Remux/Views/Terminal/TerminalRelay.swift +117 -0
- package/build.mjs +33 -0
- package/native/android/DecodeGoldenPayloads.kt +487 -0
- package/native/android/ProtocolModels.kt +188 -0
- package/native/ios/DecodeGoldenPayloads.swift +711 -0
- package/native/ios/ProtocolModels.swift +200 -0
- package/package.json +45 -0
- package/packages/RemuxKit/Package.swift +27 -0
- package/packages/RemuxKit/Sources/RemuxKit/Device/DeviceManager.swift +27 -0
- package/packages/RemuxKit/Sources/RemuxKit/Models/ProtocolModels.swift +206 -0
- package/packages/RemuxKit/Sources/RemuxKit/Networking/MessageRouter.swift +108 -0
- package/packages/RemuxKit/Sources/RemuxKit/Networking/RemuxConnection.swift +395 -0
- package/packages/RemuxKit/Sources/RemuxKit/State/RemuxState.swift +188 -0
- package/packages/RemuxKit/Sources/RemuxKit/Storage/KeychainStore.swift +142 -0
- package/packages/RemuxKit/Sources/RemuxKit/Terminal/GhosttyBridge.swift +145 -0
- package/packages/RemuxKit/Sources/RemuxKit/Terminal/GhosttyTerminalView.swift +35 -0
- package/packages/RemuxKit/Sources/RemuxKit/Terminal/Resources/ghostty-terminal.html +91 -0
- package/packages/RemuxKit/Tests/RemuxKitTests/ConnectionIntegrationTest.swift +74 -0
- package/packages/RemuxKit/Tests/RemuxKitTests/KeychainStoreTests.swift +81 -0
- package/packages/RemuxKit/Tests/RemuxKitTests/ProtocolModelsTests.swift +179 -0
- package/packages/RemuxKit/Tests/RemuxKitTests/RemuxStateTests.swift +62 -0
- package/playwright.config.ts +17 -0
- package/pnpm-lock.yaml +1588 -0
- package/pty-daemon.js +303 -0
- package/release-please-config.json +14 -0
- package/scripts/auto-deploy.sh +46 -0
- package/scripts/build-dmg.sh +121 -0
- package/scripts/build-ghostty-kit.sh +43 -0
- package/scripts/check-active-terminology.mjs +132 -0
- package/scripts/setup-ci-secrets.sh +80 -0
- package/scripts/sync-ghostty-web.sh +28 -0
- package/scripts/upload-testflight.sh +100 -0
- package/server.js +7074 -0
- package/src/adapters/agent-events.ts +246 -0
- package/src/adapters/claude-code.ts +158 -0
- package/src/adapters/codex.ts +210 -0
- package/src/adapters/generic-shell.ts +58 -0
- package/src/adapters/index.ts +15 -0
- package/src/adapters/registry.ts +99 -0
- package/src/adapters/types.ts +41 -0
- package/src/auth.ts +174 -0
- package/src/e2ee.ts +236 -0
- package/src/git-service.ts +168 -0
- package/src/message-buffer.ts +137 -0
- package/src/pty-daemon.ts +357 -0
- package/src/push.ts +127 -0
- package/src/renderers.ts +455 -0
- package/src/server.ts +2407 -0
- package/src/service.ts +226 -0
- package/src/session.ts +978 -0
- package/src/store.ts +1422 -0
- package/src/team.ts +123 -0
- package/src/tunnel.ts +126 -0
- package/src/types.d.ts +50 -0
- package/src/vt-tracker.ts +188 -0
- package/src/workspace-head.ts +144 -0
- package/src/workspace.ts +153 -0
- package/src/ws-handler.ts +1526 -0
- package/start.ps1 +83 -0
- package/tests/adapters.test.js +171 -0
- package/tests/auth.test.js +243 -0
- package/tests/codex-adapter.test.js +535 -0
- package/tests/durable-stream.test.js +153 -0
- package/tests/e2e/app.spec.js +530 -0
- package/tests/e2ee.test.js +325 -0
- package/tests/message-buffer.test.js +245 -0
- package/tests/message-routing.test.js +305 -0
- package/tests/pty-daemon.test.js +346 -0
- package/tests/push.test.js +281 -0
- package/tests/renderers.test.js +391 -0
- package/tests/search-shell.test.js +499 -0
- package/tests/server.test.js +882 -0
- package/tests/service.test.js +267 -0
- package/tests/store.test.js +369 -0
- package/tests/tunnel.test.js +67 -0
- package/tests/workspace-head.test.js +116 -0
- package/tests/workspace.test.js +417 -0
- package/tsconfig.backend.json +11 -0
- package/tsconfig.json +15 -0
- package/tui/client/client_test.go +125 -0
- package/tui/client/connection.go +342 -0
- package/tui/client/host_manager.go +141 -0
- package/tui/config/cache.go +81 -0
- package/tui/config/config.go +53 -0
- package/tui/config/config_test.go +89 -0
- package/tui/go.mod +32 -0
- package/tui/go.sum +50 -0
- package/tui/main.go +261 -0
- package/tui/tests/integration_test.go +283 -0
- package/tui/ui/model.go +310 -0
- package/vitest.config.js +10 -0
package/src/service.ts
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Launchd service management for Remux.
|
|
3
|
+
* Provides install/uninstall/status for macOS background service.
|
|
4
|
+
*
|
|
5
|
+
* Plist goes to ~/Library/LaunchAgents/com.remux.agent.plist
|
|
6
|
+
* Logs go to ~/.remux/logs/
|
|
7
|
+
*
|
|
8
|
+
* Inspired by Homebrew's launchd service pattern and similar projects
|
|
9
|
+
* (e.g. remodex) that use launchd for daemon management.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import fs from "fs";
|
|
13
|
+
import path from "path";
|
|
14
|
+
import { homedir } from "os";
|
|
15
|
+
import { execSync } from "child_process";
|
|
16
|
+
import { fileURLToPath } from "url";
|
|
17
|
+
|
|
18
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
19
|
+
const __dirname = path.dirname(__filename);
|
|
20
|
+
|
|
21
|
+
export const LABEL = "com.remux.agent";
|
|
22
|
+
export const PLIST_DIR = path.join(homedir(), "Library", "LaunchAgents");
|
|
23
|
+
export const PLIST_PATH = path.join(PLIST_DIR, `${LABEL}.plist`);
|
|
24
|
+
export const LOG_DIR = path.join(homedir(), ".remux", "logs");
|
|
25
|
+
export const SERVER_JS = path.join(__dirname, "server.js");
|
|
26
|
+
|
|
27
|
+
function escapeXml(str: string): string {
|
|
28
|
+
return str
|
|
29
|
+
.replace(/&/g, "&")
|
|
30
|
+
.replace(/</g, "<")
|
|
31
|
+
.replace(/>/g, ">")
|
|
32
|
+
.replace(/"/g, """)
|
|
33
|
+
.replace(/'/g, "'");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Generate launchd plist XML content.
|
|
38
|
+
*/
|
|
39
|
+
export function generatePlist(options: {
|
|
40
|
+
port?: number;
|
|
41
|
+
args?: string[];
|
|
42
|
+
} = {}): string {
|
|
43
|
+
const { port, args = [] } = options;
|
|
44
|
+
|
|
45
|
+
const programArgs = [process.execPath, SERVER_JS, ...args];
|
|
46
|
+
const programArgsXml = programArgs
|
|
47
|
+
.map((a) => ` <string>${escapeXml(a)}</string>`)
|
|
48
|
+
.join("\n");
|
|
49
|
+
|
|
50
|
+
// Build EnvironmentVariables section
|
|
51
|
+
const envVars: Record<string, string> = {};
|
|
52
|
+
if (port) envVars.PORT = String(port);
|
|
53
|
+
if (process.env.REMUX_TOKEN) envVars.REMUX_TOKEN = process.env.REMUX_TOKEN;
|
|
54
|
+
|
|
55
|
+
let envXml = "";
|
|
56
|
+
if (Object.keys(envVars).length > 0) {
|
|
57
|
+
const entries = Object.entries(envVars)
|
|
58
|
+
.map(
|
|
59
|
+
([k, v]) =>
|
|
60
|
+
` <key>${escapeXml(k)}</key>\n <string>${escapeXml(v)}</string>`,
|
|
61
|
+
)
|
|
62
|
+
.join("\n");
|
|
63
|
+
envXml = `
|
|
64
|
+
<key>EnvironmentVariables</key>
|
|
65
|
+
<dict>
|
|
66
|
+
${entries}
|
|
67
|
+
</dict>`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
71
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
72
|
+
<plist version="1.0">
|
|
73
|
+
<dict>
|
|
74
|
+
<key>Label</key>
|
|
75
|
+
<string>${LABEL}</string>
|
|
76
|
+
<key>ProgramArguments</key>
|
|
77
|
+
<array>
|
|
78
|
+
${programArgsXml}
|
|
79
|
+
</array>
|
|
80
|
+
<key>RunAtLoad</key>
|
|
81
|
+
<true/>
|
|
82
|
+
<key>KeepAlive</key>
|
|
83
|
+
<true/>
|
|
84
|
+
<key>WorkingDirectory</key>
|
|
85
|
+
<string>${escapeXml(__dirname)}</string>
|
|
86
|
+
<key>StandardOutPath</key>
|
|
87
|
+
<string>${escapeXml(path.join(LOG_DIR, "remux.log"))}</string>
|
|
88
|
+
<key>StandardErrorPath</key>
|
|
89
|
+
<string>${escapeXml(path.join(LOG_DIR, "remux.err"))}</string>${envXml}
|
|
90
|
+
</dict>
|
|
91
|
+
</plist>
|
|
92
|
+
`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Install and start the launchd service.
|
|
97
|
+
*/
|
|
98
|
+
export function installService(options: {
|
|
99
|
+
port?: number;
|
|
100
|
+
args?: string[];
|
|
101
|
+
} = {}): void {
|
|
102
|
+
// Create log directory
|
|
103
|
+
fs.mkdirSync(LOG_DIR, { recursive: true });
|
|
104
|
+
|
|
105
|
+
// Unload existing service if present
|
|
106
|
+
if (fs.existsSync(PLIST_PATH)) {
|
|
107
|
+
try {
|
|
108
|
+
execSync(`launchctl unload "${PLIST_PATH}"`, { stdio: "pipe" });
|
|
109
|
+
} catch {
|
|
110
|
+
// May fail if not loaded, that's fine
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Write plist
|
|
115
|
+
const xml = generatePlist(options);
|
|
116
|
+
fs.writeFileSync(PLIST_PATH, xml);
|
|
117
|
+
|
|
118
|
+
// Load service
|
|
119
|
+
execSync(`launchctl load "${PLIST_PATH}"`, { stdio: "pipe" });
|
|
120
|
+
|
|
121
|
+
console.log(`[remux] Service installed and started.`);
|
|
122
|
+
console.log(`[remux] Plist: ${PLIST_PATH}`);
|
|
123
|
+
console.log(`[remux] Logs: ${LOG_DIR}/`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Uninstall the launchd service.
|
|
128
|
+
*/
|
|
129
|
+
export function uninstallService(): void {
|
|
130
|
+
if (!fs.existsSync(PLIST_PATH)) {
|
|
131
|
+
console.log(`[remux] Service is not installed.`);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
execSync(`launchctl unload "${PLIST_PATH}"`, { stdio: "pipe" });
|
|
137
|
+
} catch {
|
|
138
|
+
// May fail if not loaded
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
fs.unlinkSync(PLIST_PATH);
|
|
142
|
+
console.log(`[remux] Service uninstalled.`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Check service status.
|
|
147
|
+
*/
|
|
148
|
+
export function serviceStatus(): {
|
|
149
|
+
installed: boolean;
|
|
150
|
+
running: boolean;
|
|
151
|
+
pid?: number;
|
|
152
|
+
} {
|
|
153
|
+
if (!fs.existsSync(PLIST_PATH)) {
|
|
154
|
+
return { installed: false, running: false };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
const output = execSync(`launchctl list ${LABEL}`, {
|
|
159
|
+
stdio: "pipe",
|
|
160
|
+
encoding: "utf8",
|
|
161
|
+
});
|
|
162
|
+
// Output format: "PID\tStatus\tLabel"
|
|
163
|
+
// PID is a number if running, "-" if not
|
|
164
|
+
const firstLine = output.trim().split("\n").pop();
|
|
165
|
+
const pid = firstLine?.split("\t")[0];
|
|
166
|
+
if (pid && pid !== "-" && !isNaN(Number(pid))) {
|
|
167
|
+
return { installed: true, running: true, pid: Number(pid) };
|
|
168
|
+
}
|
|
169
|
+
return { installed: true, running: false };
|
|
170
|
+
} catch {
|
|
171
|
+
return { installed: true, running: false };
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Handle `remux service <subcommand>` from argv.
|
|
177
|
+
* Returns true if a service command was handled (caller should exit).
|
|
178
|
+
*/
|
|
179
|
+
export function handleServiceCommand(argv: string[]): boolean {
|
|
180
|
+
// argv[0] = node, argv[1] = server.js, argv[2] = "service"
|
|
181
|
+
if (argv.length < 4 || argv[2] !== "service") return false;
|
|
182
|
+
|
|
183
|
+
const subcommand = argv[3];
|
|
184
|
+
|
|
185
|
+
switch (subcommand) {
|
|
186
|
+
case "install": {
|
|
187
|
+
const opts: { port?: number; args?: string[] } = {};
|
|
188
|
+
const portIdx = argv.indexOf("--port");
|
|
189
|
+
if (portIdx !== -1 && argv[portIdx + 1]) {
|
|
190
|
+
opts.port = Number(argv[portIdx + 1]);
|
|
191
|
+
}
|
|
192
|
+
// Collect extra args after install (excluding --port N)
|
|
193
|
+
const extra: string[] = [];
|
|
194
|
+
for (let i = 4; i < argv.length; i++) {
|
|
195
|
+
if (argv[i] === "--port") {
|
|
196
|
+
i++; // skip value
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
extra.push(argv[i]);
|
|
200
|
+
}
|
|
201
|
+
if (extra.length) opts.args = extra;
|
|
202
|
+
installService(opts);
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
case "uninstall":
|
|
206
|
+
uninstallService();
|
|
207
|
+
return true;
|
|
208
|
+
case "status": {
|
|
209
|
+
const st = serviceStatus();
|
|
210
|
+
if (!st.installed) {
|
|
211
|
+
console.log("[remux] Service is not installed.");
|
|
212
|
+
} else if (st.running) {
|
|
213
|
+
console.log(`[remux] Service is running (PID ${st.pid}).`);
|
|
214
|
+
} else {
|
|
215
|
+
console.log("[remux] Service is installed but not running.");
|
|
216
|
+
}
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
default:
|
|
220
|
+
console.error(
|
|
221
|
+
`[remux] Unknown service command: ${subcommand}\n` +
|
|
222
|
+
`Usage: remux service <install|uninstall|status>`,
|
|
223
|
+
);
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
}
|