@moxxy/cli 0.1.0 → 1.0.0
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/package.json +1 -1
- package/src/cli.js +9 -1
- package/src/commands/init.js +187 -0
- package/src/commands/settings.js +224 -0
- package/src/help.js +24 -0
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -16,6 +16,7 @@ import { runDoctor } from './commands/doctor.js';
|
|
|
16
16
|
import { runUpdate } from './commands/update.js';
|
|
17
17
|
import { runUninstall } from './commands/uninstall.js';
|
|
18
18
|
import { runPlugin } from './commands/plugin.js';
|
|
19
|
+
import { runSettings } from './commands/settings.js';
|
|
19
20
|
import { COMMAND_HELP, showHelp } from './help.js';
|
|
20
21
|
import chalk from 'chalk';
|
|
21
22
|
import { createInterface, cursorTo, clearScreenDown } from 'node:readline';
|
|
@@ -87,6 +88,9 @@ Usage:
|
|
|
87
88
|
moxxy tui [--agent <id>] Full-screen chat interface
|
|
88
89
|
moxxy chat [--agent <id>] Alias for tui
|
|
89
90
|
moxxy events tail [--agent <id>] [--run <id>] [--json]
|
|
91
|
+
moxxy settings network-mode [safe|unsafe] Get or set network mode
|
|
92
|
+
moxxy settings get [--key <k>] [--json] View settings
|
|
93
|
+
moxxy settings set --key <k> --value <v> Set a setting
|
|
90
94
|
moxxy doctor Diagnose installation
|
|
91
95
|
moxxy update [--check] [--force] [--json] Check for and install updates
|
|
92
96
|
moxxy update --rollback Restore previous gateway version
|
|
@@ -168,6 +172,9 @@ async function routeCommand(client, command, rest) {
|
|
|
168
172
|
case 'plugin':
|
|
169
173
|
await runPlugin(client, rest);
|
|
170
174
|
break;
|
|
175
|
+
case 'settings':
|
|
176
|
+
await runSettings(client, rest);
|
|
177
|
+
break;
|
|
171
178
|
case 'tui':
|
|
172
179
|
case 'chat': {
|
|
173
180
|
const { startTui } = await import('./tui/index.jsx');
|
|
@@ -217,7 +224,7 @@ async function main() {
|
|
|
217
224
|
security: { label: 'Security', hint: 'auth tokens & secrets' },
|
|
218
225
|
integrations: { label: 'Integrations', hint: 'providers, channels, MCP, plugins' },
|
|
219
226
|
tools: { label: 'Tools', hint: 'events stream' },
|
|
220
|
-
system: { label: 'System', hint: 'update & uninstall' },
|
|
227
|
+
system: { label: 'System', hint: 'settings, update & uninstall' },
|
|
221
228
|
};
|
|
222
229
|
|
|
223
230
|
const SUBMENUS = {
|
|
@@ -246,6 +253,7 @@ async function main() {
|
|
|
246
253
|
{ value: 'events', label: 'Events', hint: 'stream live events' },
|
|
247
254
|
],
|
|
248
255
|
system: [
|
|
256
|
+
{ value: 'settings', label: 'Settings', hint: 'network mode & global config' },
|
|
249
257
|
{ value: 'update', label: 'Update', hint: 'check for and install updates' },
|
|
250
258
|
{ value: 'uninstall', label: 'Uninstall', hint: 'remove all Moxxy data' },
|
|
251
259
|
],
|
package/src/commands/init.js
CHANGED
|
@@ -763,5 +763,192 @@ export async function runInit(client, args) {
|
|
|
763
763
|
}
|
|
764
764
|
}
|
|
765
765
|
|
|
766
|
+
// Step 7: Browser rendering (optional)
|
|
767
|
+
p.note(
|
|
768
|
+
'Browser rendering enables agents to load JavaScript-heavy websites\n' +
|
|
769
|
+
'using a headless Chrome browser. This requires Chrome/Chromium.\n' +
|
|
770
|
+
'Without it, agents can still fetch pages via HTTP (works for most sites).',
|
|
771
|
+
'Browser Rendering'
|
|
772
|
+
);
|
|
773
|
+
|
|
774
|
+
const enableBrowser = await p.confirm({
|
|
775
|
+
message: 'Enable browser rendering capabilities?',
|
|
776
|
+
initialValue: false,
|
|
777
|
+
});
|
|
778
|
+
handleCancel(enableBrowser);
|
|
779
|
+
|
|
780
|
+
if (enableBrowser) {
|
|
781
|
+
const chromePath = detectChromeBinary(moxxyHome);
|
|
782
|
+
|
|
783
|
+
if (chromePath) {
|
|
784
|
+
p.log.success(`Chrome found: ${chromePath}`);
|
|
785
|
+
saveBrowserRenderingSetting(moxxyHome, true);
|
|
786
|
+
p.log.success('Browser rendering enabled.');
|
|
787
|
+
} else {
|
|
788
|
+
p.log.warn('Chrome/Chromium not found on this system.');
|
|
789
|
+
|
|
790
|
+
const downloadChrome = await p.confirm({
|
|
791
|
+
message: 'Download Chromium (~150MB) to ~/.moxxy/chromium/?',
|
|
792
|
+
initialValue: true,
|
|
793
|
+
});
|
|
794
|
+
handleCancel(downloadChrome);
|
|
795
|
+
|
|
796
|
+
if (downloadChrome) {
|
|
797
|
+
const installed = await installChromium(moxxyHome);
|
|
798
|
+
if (installed) {
|
|
799
|
+
saveBrowserRenderingSetting(moxxyHome, true);
|
|
800
|
+
p.log.success('Browser rendering enabled.');
|
|
801
|
+
} else {
|
|
802
|
+
p.log.warn('Chromium install failed. You can retry later with: moxxy settings browser-rendering');
|
|
803
|
+
}
|
|
804
|
+
} else {
|
|
805
|
+
p.log.info('Skipped. Install Chrome manually or run: moxxy settings browser-rendering');
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
766
810
|
p.outro('Setup complete. Run moxxy to see available commands.');
|
|
767
811
|
}
|
|
812
|
+
|
|
813
|
+
// ---------------------------------------------------------------------------
|
|
814
|
+
// Browser rendering helpers
|
|
815
|
+
// ---------------------------------------------------------------------------
|
|
816
|
+
|
|
817
|
+
function detectChromeBinary(moxxyHome) {
|
|
818
|
+
const os = platform();
|
|
819
|
+
|
|
820
|
+
// 1. CHROME_PATH env var
|
|
821
|
+
if (process.env.CHROME_PATH && existsSync(process.env.CHROME_PATH)) {
|
|
822
|
+
return process.env.CHROME_PATH;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// 2. System Chrome
|
|
826
|
+
const systemPaths = os === 'darwin'
|
|
827
|
+
? [
|
|
828
|
+
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
|
829
|
+
'/Applications/Chromium.app/Contents/MacOS/Chromium',
|
|
830
|
+
]
|
|
831
|
+
: [
|
|
832
|
+
'/usr/bin/google-chrome',
|
|
833
|
+
'/usr/bin/google-chrome-stable',
|
|
834
|
+
'/usr/bin/chromium',
|
|
835
|
+
'/usr/bin/chromium-browser',
|
|
836
|
+
];
|
|
837
|
+
|
|
838
|
+
for (const p of systemPaths) {
|
|
839
|
+
if (existsSync(p)) return p;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
// 3. which fallback (Linux)
|
|
843
|
+
if (os === 'linux') {
|
|
844
|
+
for (const name of ['google-chrome', 'chromium-browser', 'chromium']) {
|
|
845
|
+
try {
|
|
846
|
+
const result = execSync(`which ${name}`, { stdio: 'pipe', encoding: 'utf-8' }).trim();
|
|
847
|
+
if (result && existsSync(result)) return result;
|
|
848
|
+
} catch { /* not found */ }
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// 4. Previously downloaded
|
|
853
|
+
const platDir = chromePlatformDir();
|
|
854
|
+
const binaryName = os === 'darwin'
|
|
855
|
+
? 'Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing'
|
|
856
|
+
: 'chrome';
|
|
857
|
+
const downloaded = join(moxxyHome, 'chromium', platDir, binaryName);
|
|
858
|
+
if (existsSync(downloaded)) return downloaded;
|
|
859
|
+
|
|
860
|
+
return null;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
function chromePlatformDir() {
|
|
864
|
+
const os = platform();
|
|
865
|
+
const cpuArch = arch();
|
|
866
|
+
if (os === 'darwin') {
|
|
867
|
+
return cpuArch === 'arm64' ? 'chrome-mac-arm64' : 'chrome-mac-x64';
|
|
868
|
+
}
|
|
869
|
+
return 'chrome-linux64';
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
async function installChromium(moxxyHome) {
|
|
873
|
+
const CHROME_FOR_TESTING_API = 'https://googlechromelabs.github.io/chrome-for-testing/last-known-good-versions-with-downloads.json';
|
|
874
|
+
|
|
875
|
+
try {
|
|
876
|
+
// Fetch latest stable version info
|
|
877
|
+
const versionInfo = await withSpinner('Fetching Chromium version info...', async () => {
|
|
878
|
+
const resp = await fetch(CHROME_FOR_TESTING_API, { signal: AbortSignal.timeout(15000) });
|
|
879
|
+
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
|
|
880
|
+
return resp.json();
|
|
881
|
+
}, 'Version info retrieved.');
|
|
882
|
+
|
|
883
|
+
const platKey = chromePlatformKey();
|
|
884
|
+
const stable = versionInfo.channels.Stable;
|
|
885
|
+
const chromeDownloads = stable.downloads.chrome;
|
|
886
|
+
const entry = chromeDownloads.find(d => d.platform === platKey);
|
|
887
|
+
|
|
888
|
+
if (!entry) {
|
|
889
|
+
p.log.error(`No Chromium build for platform: ${platKey}`);
|
|
890
|
+
return false;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
const downloadUrl = entry.url;
|
|
894
|
+
const targetDir = join(moxxyHome, 'chromium');
|
|
895
|
+
mkdirSync(targetDir, { recursive: true });
|
|
896
|
+
|
|
897
|
+
const zipPath = join(targetDir, 'chrome.zip');
|
|
898
|
+
|
|
899
|
+
// Download
|
|
900
|
+
await withSpinner(`Downloading Chromium ${stable.version}...`, async () => {
|
|
901
|
+
const resp = await fetch(downloadUrl, { signal: AbortSignal.timeout(300000) });
|
|
902
|
+
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
|
|
903
|
+
const fileStream = createWriteStream(zipPath);
|
|
904
|
+
await pipeline(resp.body, fileStream);
|
|
905
|
+
}, 'Download complete.');
|
|
906
|
+
|
|
907
|
+
// Extract
|
|
908
|
+
await withSpinner('Extracting Chromium...', async () => {
|
|
909
|
+
execSync(`unzip -o -q "${zipPath}" -d "${targetDir}"`, { stdio: 'pipe' });
|
|
910
|
+
}, 'Extraction complete.');
|
|
911
|
+
|
|
912
|
+
// Cleanup zip
|
|
913
|
+
try { const { unlinkSync } = await import('node:fs'); unlinkSync(zipPath); } catch { /* ignore */ }
|
|
914
|
+
|
|
915
|
+
const chromePath = detectChromeBinary(moxxyHome);
|
|
916
|
+
if (chromePath) {
|
|
917
|
+
// Make executable on Linux
|
|
918
|
+
if (platform() === 'linux') {
|
|
919
|
+
try { chmodSync(chromePath, 0o755); } catch { /* ignore */ }
|
|
920
|
+
}
|
|
921
|
+
p.log.success(`Chromium installed: ${chromePath}`);
|
|
922
|
+
return true;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
p.log.error('Extraction succeeded but Chrome binary not found');
|
|
926
|
+
return false;
|
|
927
|
+
} catch (err) {
|
|
928
|
+
p.log.error(`Failed to install Chromium: ${err.message}`);
|
|
929
|
+
return false;
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
function chromePlatformKey() {
|
|
934
|
+
const os = platform();
|
|
935
|
+
const cpuArch = arch();
|
|
936
|
+
if (os === 'darwin') {
|
|
937
|
+
return cpuArch === 'arm64' ? 'mac-arm64' : 'mac-x64';
|
|
938
|
+
}
|
|
939
|
+
return 'linux64';
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
function saveBrowserRenderingSetting(moxxyHome, enabled) {
|
|
943
|
+
const settingsFile = join(moxxyHome, 'settings.yaml');
|
|
944
|
+
let lines = [];
|
|
945
|
+
try {
|
|
946
|
+
const raw = readFileSync(settingsFile, 'utf-8');
|
|
947
|
+
lines = raw.split('\n').filter(l => !l.startsWith('browser_rendering:'));
|
|
948
|
+
} catch { /* no existing settings */ }
|
|
949
|
+
|
|
950
|
+
lines.push(`browser_rendering: ${enabled}`);
|
|
951
|
+
|
|
952
|
+
mkdirSync(moxxyHome, { recursive: true });
|
|
953
|
+
writeFileSync(settingsFile, lines.filter(l => l.trim()).join('\n') + '\n');
|
|
954
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { p, isInteractive, showResult } from '../ui.js';
|
|
2
|
+
import { parseFlags } from './auth.js';
|
|
3
|
+
import { getMoxxyHome } from './init.js';
|
|
4
|
+
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
|
|
7
|
+
const VALID_NETWORK_MODES = ['safe', 'unsafe'];
|
|
8
|
+
|
|
9
|
+
function settingsPath() {
|
|
10
|
+
return join(getMoxxyHome(), 'settings.yaml');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function loadSettings() {
|
|
14
|
+
try {
|
|
15
|
+
const raw = readFileSync(settingsPath(), 'utf-8');
|
|
16
|
+
if (!raw.trim()) return {};
|
|
17
|
+
const settings = {};
|
|
18
|
+
for (const line of raw.split('\n')) {
|
|
19
|
+
const match = line.match(/^(\w+):\s*(.+)$/);
|
|
20
|
+
if (match) settings[match[1]] = match[2].trim();
|
|
21
|
+
}
|
|
22
|
+
return settings;
|
|
23
|
+
} catch {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function saveSettings(settings) {
|
|
29
|
+
const dir = getMoxxyHome();
|
|
30
|
+
mkdirSync(dir, { recursive: true });
|
|
31
|
+
const lines = Object.entries(settings).map(([k, v]) => `${k}: ${v}`);
|
|
32
|
+
writeFileSync(settingsPath(), lines.join('\n') + '\n');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function parseSettingsCommand(args) {
|
|
36
|
+
const [action, ...rest] = args;
|
|
37
|
+
const flags = parseFlags(rest);
|
|
38
|
+
return { action, flags };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function settingsGet(flags) {
|
|
42
|
+
const settings = loadSettings();
|
|
43
|
+
const key = flags.key;
|
|
44
|
+
|
|
45
|
+
if (flags.json) {
|
|
46
|
+
console.log(JSON.stringify(key ? { key, value: settings[key] ?? null } : settings, null, 2));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (key) {
|
|
51
|
+
const value = settings[key] ?? '(not set)';
|
|
52
|
+
p.log.info(`${key}: ${value}`);
|
|
53
|
+
} else {
|
|
54
|
+
const entries = Object.entries(settings);
|
|
55
|
+
if (entries.length === 0) {
|
|
56
|
+
p.log.info('No settings configured. Using defaults.');
|
|
57
|
+
p.log.info(' network_mode: safe (default)');
|
|
58
|
+
} else {
|
|
59
|
+
for (const [k, v] of entries) {
|
|
60
|
+
p.log.info(`${k}: ${v}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function settingsSet(flags) {
|
|
67
|
+
const key = flags.key;
|
|
68
|
+
const value = flags.value;
|
|
69
|
+
|
|
70
|
+
if (!key) throw new Error('Required: --key');
|
|
71
|
+
if (value === undefined) throw new Error('Required: --value');
|
|
72
|
+
|
|
73
|
+
// Validate known keys
|
|
74
|
+
if (key === 'network_mode' && !VALID_NETWORK_MODES.includes(value)) {
|
|
75
|
+
throw new Error(`Invalid network_mode '${value}'. Must be one of: ${VALID_NETWORK_MODES.join(', ')}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const settings = loadSettings();
|
|
79
|
+
settings[key] = value;
|
|
80
|
+
saveSettings(settings);
|
|
81
|
+
|
|
82
|
+
if (flags.json) {
|
|
83
|
+
console.log(JSON.stringify({ status: 'updated', key, value }, null, 2));
|
|
84
|
+
} else {
|
|
85
|
+
showResult({ status: 'updated', key, value }, p);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function settingsNetworkMode(flags) {
|
|
90
|
+
const settings = loadSettings();
|
|
91
|
+
const current = settings.network_mode || 'safe';
|
|
92
|
+
|
|
93
|
+
// No value provided — show current or toggle interactively
|
|
94
|
+
if (!flags.mode && !flags._positional) {
|
|
95
|
+
if (isInteractive()) {
|
|
96
|
+
const selected = await p.select({
|
|
97
|
+
message: `Network mode (currently: ${current})`,
|
|
98
|
+
options: [
|
|
99
|
+
{ value: 'safe', label: 'Safe', hint: 'agent asks user before accessing non-allowlisted domains' },
|
|
100
|
+
{ value: 'unsafe', label: 'Unsafe', hint: 'domain allowlist is bypassed entirely' },
|
|
101
|
+
],
|
|
102
|
+
initialValue: current,
|
|
103
|
+
});
|
|
104
|
+
if (p.isCancel(selected)) return;
|
|
105
|
+
settings.network_mode = selected;
|
|
106
|
+
saveSettings(settings);
|
|
107
|
+
p.log.success(`Network mode set to: ${selected}`);
|
|
108
|
+
} else {
|
|
109
|
+
if (flags.json) {
|
|
110
|
+
console.log(JSON.stringify({ network_mode: current }, null, 2));
|
|
111
|
+
} else {
|
|
112
|
+
p.log.info(`network_mode: ${current}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const mode = flags.mode || flags._positional;
|
|
119
|
+
if (!VALID_NETWORK_MODES.includes(mode)) {
|
|
120
|
+
throw new Error(`Invalid mode '${mode}'. Must be one of: ${VALID_NETWORK_MODES.join(', ')}`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
settings.network_mode = mode;
|
|
124
|
+
saveSettings(settings);
|
|
125
|
+
|
|
126
|
+
if (flags.json) {
|
|
127
|
+
console.log(JSON.stringify({ status: 'updated', network_mode: mode }, null, 2));
|
|
128
|
+
} else {
|
|
129
|
+
p.log.success(`Network mode set to: ${mode}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function settingsBrowserRendering(flags) {
|
|
134
|
+
const settings = loadSettings();
|
|
135
|
+
const current = settings.browser_rendering === 'true';
|
|
136
|
+
|
|
137
|
+
// No value provided — show current or toggle interactively
|
|
138
|
+
if (!flags._positional) {
|
|
139
|
+
if (isInteractive()) {
|
|
140
|
+
const selected = await p.select({
|
|
141
|
+
message: `Browser rendering (currently: ${current ? 'enabled' : 'disabled'})`,
|
|
142
|
+
options: [
|
|
143
|
+
{ value: 'true', label: 'Enabled', hint: 'agents can render JS-heavy pages via headless Chrome' },
|
|
144
|
+
{ value: 'false', label: 'Disabled', hint: 'agents use HTTP-only browsing' },
|
|
145
|
+
],
|
|
146
|
+
initialValue: current ? 'true' : 'false',
|
|
147
|
+
});
|
|
148
|
+
if (p.isCancel(selected)) return;
|
|
149
|
+
settings.browser_rendering = selected;
|
|
150
|
+
saveSettings(settings);
|
|
151
|
+
p.log.success(`Browser rendering ${selected === 'true' ? 'enabled' : 'disabled'}.`);
|
|
152
|
+
} else {
|
|
153
|
+
if (flags.json) {
|
|
154
|
+
console.log(JSON.stringify({ browser_rendering: current }, null, 2));
|
|
155
|
+
} else {
|
|
156
|
+
p.log.info(`browser_rendering: ${current}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const val = flags._positional;
|
|
163
|
+
if (!['true', 'false', 'on', 'off', 'enable', 'disable'].includes(val)) {
|
|
164
|
+
throw new Error(`Invalid value '${val}'. Use: true/false, on/off, or enable/disable`);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const enabled = ['true', 'on', 'enable'].includes(val);
|
|
168
|
+
settings.browser_rendering = String(enabled);
|
|
169
|
+
saveSettings(settings);
|
|
170
|
+
|
|
171
|
+
if (flags.json) {
|
|
172
|
+
console.log(JSON.stringify({ status: 'updated', browser_rendering: enabled }, null, 2));
|
|
173
|
+
} else {
|
|
174
|
+
p.log.success(`Browser rendering ${enabled ? 'enabled' : 'disabled'}.`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export async function runSettings(_client, args) {
|
|
179
|
+
const { action, flags } = parseSettingsCommand(args);
|
|
180
|
+
|
|
181
|
+
// Collect first positional arg after the action for convenience
|
|
182
|
+
// e.g. `moxxy settings network-mode unsafe`
|
|
183
|
+
const restArgs = args.slice(1).filter(a => !a.startsWith('--'));
|
|
184
|
+
if (restArgs.length > 0 && !flags._positional) {
|
|
185
|
+
flags._positional = restArgs[0];
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
switch (action) {
|
|
189
|
+
case 'get':
|
|
190
|
+
await settingsGet(flags);
|
|
191
|
+
break;
|
|
192
|
+
case 'set':
|
|
193
|
+
await settingsSet(flags);
|
|
194
|
+
break;
|
|
195
|
+
case 'network-mode':
|
|
196
|
+
await settingsNetworkMode(flags);
|
|
197
|
+
break;
|
|
198
|
+
case 'browser-rendering':
|
|
199
|
+
await settingsBrowserRendering(flags);
|
|
200
|
+
break;
|
|
201
|
+
default:
|
|
202
|
+
if (isInteractive() && !action) {
|
|
203
|
+
// Interactive: show settings menu
|
|
204
|
+
const selected = await p.select({
|
|
205
|
+
message: 'Settings',
|
|
206
|
+
options: [
|
|
207
|
+
{ value: 'network-mode', label: 'Network mode', hint: 'safe / unsafe domain access' },
|
|
208
|
+
{ value: 'browser-rendering', label: 'Browser rendering', hint: 'headless Chrome for JS-heavy sites' },
|
|
209
|
+
{ value: 'get', label: 'View all settings', hint: 'show current configuration' },
|
|
210
|
+
],
|
|
211
|
+
});
|
|
212
|
+
if (p.isCancel(selected)) return;
|
|
213
|
+
await runSettings(_client, [selected]);
|
|
214
|
+
} else {
|
|
215
|
+
throw new Error(
|
|
216
|
+
'Usage: moxxy settings <action>\n' +
|
|
217
|
+
' network-mode [safe|unsafe] Get or set network mode\n' +
|
|
218
|
+
' browser-rendering [true|false] Enable/disable headless Chrome rendering\n' +
|
|
219
|
+
' get [--key <k>] View settings\n' +
|
|
220
|
+
' set --key <k> --value <v> Set a setting'
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
package/src/help.js
CHANGED
|
@@ -1,4 +1,28 @@
|
|
|
1
1
|
export const COMMAND_HELP = {
|
|
2
|
+
settings: `Usage: moxxy settings <action> [options]
|
|
3
|
+
|
|
4
|
+
Manage global Moxxy settings.
|
|
5
|
+
|
|
6
|
+
Actions:
|
|
7
|
+
network-mode [safe|unsafe] Get or set network mode
|
|
8
|
+
get [--key <k>] View all settings or a single key
|
|
9
|
+
set --key <k> --value <v> Set a setting value
|
|
10
|
+
|
|
11
|
+
Network Modes:
|
|
12
|
+
safe (default) Agent asks the user before accessing non-allowlisted domains
|
|
13
|
+
unsafe Domain allowlist is bypassed entirely — any domain is allowed
|
|
14
|
+
|
|
15
|
+
Options:
|
|
16
|
+
--json Output as JSON
|
|
17
|
+
|
|
18
|
+
Examples:
|
|
19
|
+
moxxy settings network-mode Show current mode
|
|
20
|
+
moxxy settings network-mode unsafe Switch to unsafe mode
|
|
21
|
+
moxxy settings network-mode safe Switch back to safe mode
|
|
22
|
+
moxxy settings get Show all settings
|
|
23
|
+
moxxy settings get --key network_mode Show a single setting
|
|
24
|
+
moxxy settings set --key network_mode --value unsafe`,
|
|
25
|
+
|
|
2
26
|
init: `Usage: moxxy init
|
|
3
27
|
|
|
4
28
|
First-time setup wizard. Configures the Moxxy home directory, auth mode,
|