@moxxy/cli 0.0.12 → 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/README.md +278 -112
- package/bin/moxxy +10 -0
- package/package.json +36 -53
- package/src/api-client.js +286 -0
- package/src/cli.js +349 -0
- package/src/commands/agent.js +413 -0
- package/src/commands/auth.js +326 -0
- package/src/commands/channel.js +285 -0
- package/src/commands/doctor.js +261 -0
- package/src/commands/events.js +80 -0
- package/src/commands/gateway.js +428 -0
- package/src/commands/heartbeat.js +145 -0
- package/src/commands/init.js +954 -0
- package/src/commands/mcp.js +278 -0
- package/src/commands/plugin.js +583 -0
- package/src/commands/provider.js +1934 -0
- package/src/commands/settings.js +224 -0
- package/src/commands/skill.js +125 -0
- package/src/commands/template.js +237 -0
- package/src/commands/uninstall.js +196 -0
- package/src/commands/update.js +406 -0
- package/src/commands/vault.js +219 -0
- package/src/help.js +392 -0
- package/src/lib/plugin-registry.js +98 -0
- package/src/platform.js +40 -0
- package/src/sse-client.js +79 -0
- package/src/tui/action-wizards.js +130 -0
- package/src/tui/app.jsx +859 -0
- package/src/tui/components/action-picker.jsx +86 -0
- package/src/tui/components/chat-panel.jsx +120 -0
- package/src/tui/components/footer.jsx +13 -0
- package/src/tui/components/header.jsx +45 -0
- package/src/tui/components/input-area.jsx +384 -0
- package/src/tui/components/messages/ask-message.jsx +13 -0
- package/src/tui/components/messages/assistant-message.jsx +165 -0
- package/src/tui/components/messages/channel-message.jsx +18 -0
- package/src/tui/components/messages/event-message.jsx +22 -0
- package/src/tui/components/messages/hive-status.jsx +34 -0
- package/src/tui/components/messages/skill-message.jsx +31 -0
- package/src/tui/components/messages/system-message.jsx +12 -0
- package/src/tui/components/messages/thinking.jsx +25 -0
- package/src/tui/components/messages/tool-group.jsx +62 -0
- package/src/tui/components/messages/tool-message.jsx +66 -0
- package/src/tui/components/messages/user-message.jsx +12 -0
- package/src/tui/components/model-picker.jsx +138 -0
- package/src/tui/components/multiline-input.jsx +72 -0
- package/src/tui/events-handler.js +730 -0
- package/src/tui/helpers.js +59 -0
- package/src/tui/hooks/use-command-handler.js +451 -0
- package/src/tui/index.jsx +55 -0
- package/src/tui/input-utils.js +26 -0
- package/src/tui/markdown-renderer.js +66 -0
- package/src/tui/mcp-wizard.js +136 -0
- package/src/tui/model-picker.js +174 -0
- package/src/tui/slash-commands.js +26 -0
- package/src/tui/store.js +12 -0
- package/src/tui/theme.js +17 -0
- package/src/ui.js +109 -0
- package/bin/moxxy.js +0 -2
- package/dist/chunk-23LZYKQ6.mjs +0 -1131
- package/dist/chunk-2FZEA3NG.mjs +0 -457
- package/dist/chunk-3KDPLS22.mjs +0 -1131
- package/dist/chunk-3QRJTRBT.mjs +0 -1102
- package/dist/chunk-6DZX6EAA.mjs +0 -37
- package/dist/chunk-A4WRDUNY.mjs +0 -1242
- package/dist/chunk-C46NSEKG.mjs +0 -211
- package/dist/chunk-CAUXONEF.mjs +0 -1131
- package/dist/chunk-CPL5V56X.mjs +0 -1131
- package/dist/chunk-CTBVTTBG.mjs +0 -440
- package/dist/chunk-FHHLXTEZ.mjs +0 -1121
- package/dist/chunk-FXY3GPVA.mjs +0 -1126
- package/dist/chunk-GSNMMI3H.mjs +0 -530
- package/dist/chunk-HHOAOGUS.mjs +0 -1242
- package/dist/chunk-ITBO7BKI.mjs +0 -1243
- package/dist/chunk-J33O35WX.mjs +0 -532
- package/dist/chunk-N5JTPB6U.mjs +0 -820
- package/dist/chunk-NGVL4Q5C.mjs +0 -1102
- package/dist/chunk-Q2OCMNYI.mjs +0 -1131
- package/dist/chunk-QDVRLN6D.mjs +0 -1121
- package/dist/chunk-QO2JONHP.mjs +0 -1131
- package/dist/chunk-RVAPILHA.mjs +0 -1242
- package/dist/chunk-S7YBOV7E.mjs +0 -1131
- package/dist/chunk-SHIG6Y5L.mjs +0 -1074
- package/dist/chunk-SOFST2PV.mjs +0 -1242
- package/dist/chunk-SUNUYS6G.mjs +0 -1243
- package/dist/chunk-TMZWETMH.mjs +0 -1242
- package/dist/chunk-TYD7NMMI.mjs +0 -581
- package/dist/chunk-TYQ3YS42.mjs +0 -1068
- package/dist/chunk-UALWCJ7F.mjs +0 -1131
- package/dist/chunk-UQZKODNW.mjs +0 -1124
- package/dist/chunk-USC6R2ON.mjs +0 -1242
- package/dist/chunk-W32EQCVC.mjs +0 -823
- package/dist/chunk-WMB5ENMC.mjs +0 -1242
- package/dist/chunk-WNHA5JAP.mjs +0 -1242
- package/dist/cli-2AIWTL6F.mjs +0 -8
- package/dist/cli-2QKJ5UUL.mjs +0 -8
- package/dist/cli-4RIS6DQX.mjs +0 -8
- package/dist/cli-5RH4VBBL.mjs +0 -7
- package/dist/cli-7MK4YGOP.mjs +0 -7
- package/dist/cli-B4KH6MZI.mjs +0 -8
- package/dist/cli-CGO2LZ6Z.mjs +0 -8
- package/dist/cli-CVP26EL2.mjs +0 -8
- package/dist/cli-DDRVVNAV.mjs +0 -8
- package/dist/cli-E7U56QVQ.mjs +0 -8
- package/dist/cli-EQNRMLL3.mjs +0 -8
- package/dist/cli-F5RUHHH4.mjs +0 -8
- package/dist/cli-LX6FFSEF.mjs +0 -8
- package/dist/cli-LY74GWKR.mjs +0 -6
- package/dist/cli-MAT3ZJHI.mjs +0 -8
- package/dist/cli-NJXXTQYF.mjs +0 -8
- package/dist/cli-O4ZGFAZG.mjs +0 -8
- package/dist/cli-ORVLI3UQ.mjs +0 -8
- package/dist/cli-PV43ZVKA.mjs +0 -8
- package/dist/cli-REVD6ISM.mjs +0 -8
- package/dist/cli-TBX76KQX.mjs +0 -8
- package/dist/cli-THCGF7SQ.mjs +0 -8
- package/dist/cli-TLX5ENVM.mjs +0 -8
- package/dist/cli-TMNI5ZYE.mjs +0 -8
- package/dist/cli-TNJHCBQA.mjs +0 -6
- package/dist/cli-TUX22CZP.mjs +0 -8
- package/dist/cli-XJVH7EEP.mjs +0 -8
- package/dist/cli-XXOW4VXJ.mjs +0 -8
- package/dist/cli-XZ5RESNB.mjs +0 -6
- package/dist/cli-YCBYZ76Q.mjs +0 -8
- package/dist/cli-ZLMQCU7X.mjs +0 -8
- package/dist/dist-2VGKJRBH.mjs +0 -6820
- package/dist/dist-37BNX4QG.mjs +0 -7081
- package/dist/dist-7LTHRYKA.mjs +0 -11569
- package/dist/dist-7XJPQW5C.mjs +0 -6950
- package/dist/dist-AYMVOW7T.mjs +0 -7123
- package/dist/dist-BHUWCDRS.mjs +0 -7132
- package/dist/dist-FAXRJMEN.mjs +0 -6812
- package/dist/dist-HQGANM3P.mjs +0 -6976
- package/dist/dist-KATLOZQV.mjs +0 -7054
- package/dist/dist-KLSB6YHV.mjs +0 -6964
- package/dist/dist-LKIOZQ42.mjs +0 -17
- package/dist/dist-UYA4RJUH.mjs +0 -2792
- package/dist/dist-ZYHCBILM.mjs +0 -6993
- package/dist/index.d.mts +0 -23
- package/dist/index.d.ts +0 -23
- package/dist/index.js +0 -25531
- package/dist/index.mjs +0 -18
- package/dist/src-APP5P3UD.mjs +0 -1386
- package/dist/src-D5HMDDVE.mjs +0 -1324
- package/dist/src-EK3WD4AU.mjs +0 -1327
- package/dist/src-LSZFLMFN.mjs +0 -1400
- package/dist/src-T77DFTFP.mjs +0 -1407
- package/dist/src-WIOCZRAC.mjs +0 -1397
- package/dist/src-YK6CHCMW.mjs +0 -1400
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vault commands: add/grant/revoke/list.
|
|
3
|
+
*/
|
|
4
|
+
import { parseFlags } from './auth.js';
|
|
5
|
+
import { isInteractive, handleCancel, withSpinner, showResult, pickAgent, p } from '../ui.js';
|
|
6
|
+
|
|
7
|
+
export async function runVault(client, args) {
|
|
8
|
+
let [action, ...rest] = args;
|
|
9
|
+
const flags = parseFlags(rest);
|
|
10
|
+
|
|
11
|
+
// Interactive sub-menu when no valid action
|
|
12
|
+
if (!['add', 'remove', 'grant', 'revoke', 'list'].includes(action) && isInteractive()) {
|
|
13
|
+
action = await p.select({
|
|
14
|
+
message: 'Vault action',
|
|
15
|
+
options: [
|
|
16
|
+
{ value: 'add', label: 'Add secret', hint: 'register a new secret' },
|
|
17
|
+
{ value: 'remove', label: 'Remove secret', hint: 'delete a secret from the vault' },
|
|
18
|
+
{ value: 'grant', label: 'Grant access', hint: 'grant agent access to a secret' },
|
|
19
|
+
{ value: 'revoke', label: 'Revoke access', hint: 'revoke agent secret access' },
|
|
20
|
+
{ value: 'list', label: 'List secrets', hint: 'show all secrets' },
|
|
21
|
+
],
|
|
22
|
+
});
|
|
23
|
+
handleCancel(action);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
switch (action) {
|
|
27
|
+
case 'add': {
|
|
28
|
+
// Interactive wizard when missing required fields
|
|
29
|
+
if ((!flags.key && !flags.name) && isInteractive()) {
|
|
30
|
+
const keyName = handleCancel(await p.text({
|
|
31
|
+
message: 'Secret key name',
|
|
32
|
+
placeholder: 'OPENAI_API_KEY',
|
|
33
|
+
validate: (val) => { if (!val) return 'Key name is required'; },
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
const backendKey = handleCancel(await p.text({
|
|
37
|
+
message: 'Backend key reference',
|
|
38
|
+
placeholder: 'env:OPENAI_API_KEY',
|
|
39
|
+
validate: (val) => { if (!val) return 'Backend key is required'; },
|
|
40
|
+
}));
|
|
41
|
+
|
|
42
|
+
const policyLabel = handleCancel(await p.text({
|
|
43
|
+
message: 'Policy label',
|
|
44
|
+
placeholder: 'optional',
|
|
45
|
+
}));
|
|
46
|
+
|
|
47
|
+
const body = {
|
|
48
|
+
key_name: keyName,
|
|
49
|
+
backend_key: backendKey,
|
|
50
|
+
};
|
|
51
|
+
if (policyLabel) body.policy_label = policyLabel;
|
|
52
|
+
|
|
53
|
+
const result = await withSpinner('Adding secret...', () =>
|
|
54
|
+
client.request('/v1/vault/secrets', 'POST', body), 'Secret added.');
|
|
55
|
+
|
|
56
|
+
showResult('Secret Added', {
|
|
57
|
+
ID: result.id || result.secret_ref_id,
|
|
58
|
+
Key: keyName,
|
|
59
|
+
Backend: backendKey,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const grantNow = await p.confirm({
|
|
63
|
+
message: 'Grant to an agent?',
|
|
64
|
+
initialValue: false,
|
|
65
|
+
});
|
|
66
|
+
handleCancel(grantNow);
|
|
67
|
+
|
|
68
|
+
if (grantNow) {
|
|
69
|
+
const agentId = await pickAgent(client, 'Select agent to grant');
|
|
70
|
+
const secretId = result.id || result.secret_ref_id;
|
|
71
|
+
if (secretId) {
|
|
72
|
+
await withSpinner('Granting access...', () =>
|
|
73
|
+
client.request('/v1/vault/grants', 'POST', {
|
|
74
|
+
agent_id: agentId,
|
|
75
|
+
secret_ref_id: secretId,
|
|
76
|
+
}), 'Access granted.');
|
|
77
|
+
} else {
|
|
78
|
+
p.log.warn('Could not determine secret ID for grant.');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const body = {
|
|
86
|
+
key_name: flags.key || flags.name,
|
|
87
|
+
backend_key: flags.backend,
|
|
88
|
+
};
|
|
89
|
+
if (flags.label) body.policy_label = flags.label;
|
|
90
|
+
if (!body.key_name || !body.backend_key) {
|
|
91
|
+
throw new Error('Required: --key, --backend');
|
|
92
|
+
}
|
|
93
|
+
const result = await client.request('/v1/vault/secrets', 'POST', body);
|
|
94
|
+
console.log(JSON.stringify(result, null, 2));
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
case 'remove': {
|
|
99
|
+
let secretId = flags.id || flags.secret;
|
|
100
|
+
|
|
101
|
+
if (!secretId && isInteractive()) {
|
|
102
|
+
const secrets = await withSpinner('Fetching secrets...', () =>
|
|
103
|
+
client.listSecrets(), 'Secrets loaded.');
|
|
104
|
+
|
|
105
|
+
if (!Array.isArray(secrets) || secrets.length === 0) {
|
|
106
|
+
p.log.warn('No secrets to remove.');
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
secretId = handleCancel(await p.select({
|
|
111
|
+
message: 'Select secret to remove',
|
|
112
|
+
options: secrets.map(s => ({
|
|
113
|
+
value: s.id,
|
|
114
|
+
label: s.key_name,
|
|
115
|
+
hint: `${s.id.slice(0, 12)} backend=${s.backend_key}`,
|
|
116
|
+
})),
|
|
117
|
+
}));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!secretId) throw new Error('Required: --id');
|
|
121
|
+
|
|
122
|
+
if (isInteractive()) {
|
|
123
|
+
await withSpinner('Removing secret...', () =>
|
|
124
|
+
client.deleteSecret(secretId), 'Secret removed.');
|
|
125
|
+
} else {
|
|
126
|
+
await client.deleteSecret(secretId);
|
|
127
|
+
console.log(`Secret ${secretId} removed.`);
|
|
128
|
+
}
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
case 'grant': {
|
|
133
|
+
if (!flags.agent || !flags.secret) {
|
|
134
|
+
throw new Error('Required: --agent, --secret');
|
|
135
|
+
}
|
|
136
|
+
const body = {
|
|
137
|
+
agent_id: flags.agent,
|
|
138
|
+
secret_ref_id: flags.secret,
|
|
139
|
+
};
|
|
140
|
+
const result = await client.request('/v1/vault/grants', 'POST', body);
|
|
141
|
+
console.log(`Grant created for agent ${flags.agent}.`);
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
case 'list': {
|
|
146
|
+
if (isInteractive()) {
|
|
147
|
+
const secrets = await withSpinner('Fetching secrets...', () =>
|
|
148
|
+
client.listSecrets(), 'Secrets loaded.');
|
|
149
|
+
const grants = await withSpinner('Fetching grants...', () =>
|
|
150
|
+
client.listGrants(), 'Grants loaded.');
|
|
151
|
+
|
|
152
|
+
if (Array.isArray(secrets) && secrets.length > 0) {
|
|
153
|
+
p.log.info('\u2500\u2500 Secrets \u2500\u2500');
|
|
154
|
+
for (const s of secrets) {
|
|
155
|
+
p.log.info(` ${s.key_name} (${s.id.slice(0, 12)}) backend=${s.backend_key}`);
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
p.log.warn('No secrets found.');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (Array.isArray(grants) && grants.length > 0) {
|
|
162
|
+
p.log.info('\u2500\u2500 Grants \u2500\u2500');
|
|
163
|
+
for (const g of grants) {
|
|
164
|
+
const status = g.revoked_at ? 'revoked' : 'active';
|
|
165
|
+
p.log.info(` agent=${g.agent_id.slice(0, 12)} secret=${g.secret_ref_id.slice(0, 12)} [${status}]`);
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
p.log.info('No grants found.');
|
|
169
|
+
}
|
|
170
|
+
} else {
|
|
171
|
+
const secrets = await client.listSecrets();
|
|
172
|
+
const grants = await client.listGrants();
|
|
173
|
+
console.log(JSON.stringify({ secrets, grants }, null, 2));
|
|
174
|
+
}
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
case 'revoke': {
|
|
179
|
+
let grantId = flags.id || flags.grant;
|
|
180
|
+
|
|
181
|
+
if (!grantId && isInteractive()) {
|
|
182
|
+
const grants = await withSpinner('Fetching grants...', () =>
|
|
183
|
+
client.listGrants(), 'Grants loaded.');
|
|
184
|
+
|
|
185
|
+
const active = (grants || []).filter(g => !g.revoked_at);
|
|
186
|
+
if (active.length === 0) {
|
|
187
|
+
p.log.warn('No active grants to revoke.');
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
grantId = handleCancel(await p.select({
|
|
192
|
+
message: 'Select grant to revoke',
|
|
193
|
+
options: active.map(g => ({
|
|
194
|
+
value: g.id,
|
|
195
|
+
label: `agent=${g.agent_id.slice(0, 12)} secret=${g.secret_ref_id.slice(0, 12)}`,
|
|
196
|
+
hint: g.id.slice(0, 12),
|
|
197
|
+
})),
|
|
198
|
+
}));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (!grantId) throw new Error('Required: --id');
|
|
202
|
+
|
|
203
|
+
if (isInteractive()) {
|
|
204
|
+
await withSpinner('Revoking grant...', () =>
|
|
205
|
+
client.revokeGrant(grantId), 'Grant revoked.');
|
|
206
|
+
} else {
|
|
207
|
+
await client.revokeGrant(grantId);
|
|
208
|
+
console.log(`Grant ${grantId} revoked.`);
|
|
209
|
+
}
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
default: {
|
|
214
|
+
const { showHelp } = await import('../help.js');
|
|
215
|
+
showHelp('vault', p);
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
package/src/help.js
ADDED
|
@@ -0,0 +1,392 @@
|
|
|
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
|
+
|
|
26
|
+
init: `Usage: moxxy init
|
|
27
|
+
|
|
28
|
+
First-time setup wizard. Configures the Moxxy home directory, auth mode,
|
|
29
|
+
API token, and optional channel setup.
|
|
30
|
+
|
|
31
|
+
Steps:
|
|
32
|
+
1. Creates ~/.moxxy directory structure
|
|
33
|
+
2. Configures gateway URL
|
|
34
|
+
3. Selects auth mode (token or loopback)
|
|
35
|
+
4. Bootstraps an API token
|
|
36
|
+
5. Optionally sets up a Telegram/Discord channel`,
|
|
37
|
+
|
|
38
|
+
auth: `Usage: moxxy auth token <action> [options]
|
|
39
|
+
|
|
40
|
+
Manage API tokens for gateway authentication.
|
|
41
|
+
|
|
42
|
+
Actions:
|
|
43
|
+
create Create a new API token
|
|
44
|
+
list List all tokens
|
|
45
|
+
revoke Revoke an existing token
|
|
46
|
+
|
|
47
|
+
Options:
|
|
48
|
+
--scopes <s> Comma-separated scopes (e.g. "*", "agents:read,runs:write")
|
|
49
|
+
--ttl <seconds> Token time-to-live in seconds (omit for no expiry)
|
|
50
|
+
--description <d> Optional token description
|
|
51
|
+
--json Output as JSON
|
|
52
|
+
|
|
53
|
+
Valid scopes:
|
|
54
|
+
* agents:read agents:write runs:write vault:read vault:write
|
|
55
|
+
tokens:admin events:read channels:read channels:write
|
|
56
|
+
|
|
57
|
+
Examples:
|
|
58
|
+
moxxy auth token create --scopes "*"
|
|
59
|
+
moxxy auth token create --scopes "agents:read,runs:write" --ttl 86400
|
|
60
|
+
moxxy auth token list --json
|
|
61
|
+
moxxy auth token revoke <token-id>`,
|
|
62
|
+
|
|
63
|
+
agent: `Usage: moxxy agent <action> [options]
|
|
64
|
+
|
|
65
|
+
Create and manage agents.
|
|
66
|
+
|
|
67
|
+
Actions:
|
|
68
|
+
create Provision a new agent
|
|
69
|
+
run Start a task run on an agent
|
|
70
|
+
stop Stop a running agent
|
|
71
|
+
status Check agent status
|
|
72
|
+
update Change provider, model, or temperature
|
|
73
|
+
delete Permanently remove an agent
|
|
74
|
+
|
|
75
|
+
Options:
|
|
76
|
+
--provider <id> Provider ID (e.g. openai, anthropic)
|
|
77
|
+
--model <id> Model ID (e.g. gpt-4o, claude-sonnet-4-20250514)
|
|
78
|
+
--name <name> Agent name (create only)
|
|
79
|
+
--persona <text> Agent persona/system prompt (create only)
|
|
80
|
+
--temperature <n> Sampling temperature (default: 0.7)
|
|
81
|
+
--id <name> Agent name (run/stop/status/update/delete)
|
|
82
|
+
--task <text> Task description (run only)
|
|
83
|
+
--policy <profile> Policy profile name (create only)
|
|
84
|
+
--json Output as JSON
|
|
85
|
+
|
|
86
|
+
Examples:
|
|
87
|
+
moxxy agent create --name my-agent --provider openai --model gpt-4o
|
|
88
|
+
moxxy agent run --id my-agent --task "Summarize the README"
|
|
89
|
+
moxxy agent status --id my-agent --json
|
|
90
|
+
moxxy agent stop --id my-agent
|
|
91
|
+
moxxy agent update --id my-agent --model gpt-4o-mini
|
|
92
|
+
moxxy agent delete --id my-agent`,
|
|
93
|
+
|
|
94
|
+
provider: `Usage: moxxy provider <action> [options]
|
|
95
|
+
|
|
96
|
+
Manage LLM providers.
|
|
97
|
+
|
|
98
|
+
Actions:
|
|
99
|
+
install Add a built-in or custom provider
|
|
100
|
+
login OAuth/subscription login (currently openai-codex)
|
|
101
|
+
list Show installed providers
|
|
102
|
+
|
|
103
|
+
Options:
|
|
104
|
+
--id <id> Provider ID for install (e.g. openai, anthropic, xai)
|
|
105
|
+
--method <m> Login method for OAuth providers: browser | headless
|
|
106
|
+
--no-browser Do not auto-open browser (print URL only)
|
|
107
|
+
--originator <id> Advanced: override OAuth originator (default: Codex Desktop)
|
|
108
|
+
--allowed_workspace_id <id> Advanced: constrain browser OAuth to a workspace/org id
|
|
109
|
+
--organization_id <id> Advanced: pass explicit organization id in OAuth URL
|
|
110
|
+
--project_id <id> Advanced: pass explicit project id in OAuth URL
|
|
111
|
+
--api_key <key> Fallback: manually provide OpenAI API key if OAuth issuance fails
|
|
112
|
+
(automatic fallback) If API-key issuance fails, uses ChatGPT OAuth session mode
|
|
113
|
+
--model <id> Custom model ID to add
|
|
114
|
+
--name <name> Display name (custom providers)
|
|
115
|
+
--api_base <url> API base URL (custom providers)
|
|
116
|
+
--json Output as JSON
|
|
117
|
+
|
|
118
|
+
Built-in providers:
|
|
119
|
+
anthropic Anthropic (Claude models)
|
|
120
|
+
openai OpenAI (GPT models)
|
|
121
|
+
openai-codex OpenAI subscription OAuth login
|
|
122
|
+
xai xAI (Grok models)
|
|
123
|
+
google Google (Gemini models)
|
|
124
|
+
deepseek DeepSeek
|
|
125
|
+
|
|
126
|
+
Examples:
|
|
127
|
+
moxxy provider list
|
|
128
|
+
moxxy provider install --id openai
|
|
129
|
+
moxxy provider login --id openai-codex --method browser
|
|
130
|
+
moxxy provider login --id openai-codex --method headless --no-browser
|
|
131
|
+
moxxy provider login --id openai-codex --method browser --api_key sk-...
|
|
132
|
+
moxxy provider install --id anthropic --model claude-sonnet-4-20250514`,
|
|
133
|
+
|
|
134
|
+
skill: `Usage: moxxy skill <action> [options]
|
|
135
|
+
|
|
136
|
+
Create and manage agent skills.
|
|
137
|
+
|
|
138
|
+
Actions:
|
|
139
|
+
create Create a skill on an agent
|
|
140
|
+
remove Remove a skill from an agent
|
|
141
|
+
list List skills for an agent
|
|
142
|
+
|
|
143
|
+
Options:
|
|
144
|
+
--agent <id> Agent ID
|
|
145
|
+
--skill <id> Skill ID (remove)
|
|
146
|
+
--content <c> Skill content/markdown (create)
|
|
147
|
+
|
|
148
|
+
Examples:
|
|
149
|
+
moxxy skill create --agent <id> --content "..."
|
|
150
|
+
moxxy skill list --agent <id>
|
|
151
|
+
moxxy skill remove --agent <id> --skill <skill-id>`,
|
|
152
|
+
|
|
153
|
+
template: `Usage: moxxy template <action> [options]
|
|
154
|
+
|
|
155
|
+
Manage agent templates (archetypes).
|
|
156
|
+
|
|
157
|
+
Actions:
|
|
158
|
+
list List all templates
|
|
159
|
+
get View template details
|
|
160
|
+
create Create a new template
|
|
161
|
+
update Update an existing template
|
|
162
|
+
remove Delete a template
|
|
163
|
+
assign Assign a template to an agent
|
|
164
|
+
|
|
165
|
+
Options:
|
|
166
|
+
--slug <slug> Template slug (get/update/remove)
|
|
167
|
+
--content <c> Template content (create/update)
|
|
168
|
+
--agent <id> Agent ID (assign)
|
|
169
|
+
--template <slug> Template slug (assign)
|
|
170
|
+
|
|
171
|
+
Examples:
|
|
172
|
+
moxxy template list
|
|
173
|
+
moxxy template get builder
|
|
174
|
+
moxxy template create --content "---\\nname: Custom\\n..."
|
|
175
|
+
moxxy template assign --agent my-agent --template builder
|
|
176
|
+
moxxy template remove builder`,
|
|
177
|
+
|
|
178
|
+
heartbeat: `Usage: moxxy heartbeat <action> [options]
|
|
179
|
+
|
|
180
|
+
Schedule recurring heartbeat rules for agents.
|
|
181
|
+
|
|
182
|
+
Actions:
|
|
183
|
+
set Configure a heartbeat rule
|
|
184
|
+
list Show heartbeat rules for an agent
|
|
185
|
+
disable Disable a heartbeat rule
|
|
186
|
+
|
|
187
|
+
Options:
|
|
188
|
+
--agent <id> Agent ID
|
|
189
|
+
--interval <min> Interval in minutes (default: 5)
|
|
190
|
+
--action_type <t> Action type: notify_cli, webhook, restart
|
|
191
|
+
--payload <data> Webhook URL or payload (webhook action_type)
|
|
192
|
+
--id <id> Heartbeat ID (disable)
|
|
193
|
+
|
|
194
|
+
Examples:
|
|
195
|
+
moxxy heartbeat set --agent <id> --interval 10 --action_type notify_cli
|
|
196
|
+
moxxy heartbeat set --agent <id> --interval 30 --action_type webhook --payload https://...
|
|
197
|
+
moxxy heartbeat list --agent <id>
|
|
198
|
+
moxxy heartbeat disable --agent <id> --id <heartbeat-id>`,
|
|
199
|
+
|
|
200
|
+
vault: `Usage: moxxy vault <action> [options]
|
|
201
|
+
|
|
202
|
+
Manage secrets and access grants.
|
|
203
|
+
|
|
204
|
+
Actions:
|
|
205
|
+
add Register a new secret reference
|
|
206
|
+
grant Grant an agent access to a secret
|
|
207
|
+
revoke Revoke an agent's secret access
|
|
208
|
+
list Show all secrets and grants
|
|
209
|
+
|
|
210
|
+
Options:
|
|
211
|
+
--key <name> Secret key name (e.g. OPENAI_API_KEY)
|
|
212
|
+
--backend <key> Backend key reference (e.g. env:OPENAI_API_KEY)
|
|
213
|
+
--label <label> Policy label (optional)
|
|
214
|
+
--agent <id> Agent ID (grant)
|
|
215
|
+
--secret <id> Secret ref ID (grant)
|
|
216
|
+
--id <id> Grant ID (revoke)
|
|
217
|
+
|
|
218
|
+
Examples:
|
|
219
|
+
moxxy vault add --key OPENAI_API_KEY --backend env:OPENAI_API_KEY
|
|
220
|
+
moxxy vault grant --agent <agent-id> --secret <secret-id>
|
|
221
|
+
moxxy vault list
|
|
222
|
+
moxxy vault revoke --id <grant-id>`,
|
|
223
|
+
|
|
224
|
+
channel: `Usage: moxxy channel <action> [options]
|
|
225
|
+
|
|
226
|
+
Manage messaging channels (Telegram, Discord).
|
|
227
|
+
|
|
228
|
+
Actions:
|
|
229
|
+
create Create a new channel
|
|
230
|
+
list List all channels
|
|
231
|
+
pair --code <code> --agent <id> Pair a chat to an agent
|
|
232
|
+
delete <id> Delete a channel
|
|
233
|
+
bindings <id> List bindings for a channel
|
|
234
|
+
unbind <channel-id> <binding-id> Unbind a chat
|
|
235
|
+
|
|
236
|
+
Options:
|
|
237
|
+
--code <code> 6-digit pairing code from the bot
|
|
238
|
+
--agent <id> Agent ID to bind
|
|
239
|
+
--json Output as JSON
|
|
240
|
+
|
|
241
|
+
Examples:
|
|
242
|
+
moxxy channel create
|
|
243
|
+
moxxy channel list
|
|
244
|
+
moxxy channel pair --code 123456 --agent <agent-id>
|
|
245
|
+
moxxy channel delete <channel-id>
|
|
246
|
+
moxxy channel bindings <channel-id>
|
|
247
|
+
moxxy channel unbind <channel-id> <binding-id>`,
|
|
248
|
+
|
|
249
|
+
events: `Usage: moxxy events tail [options]
|
|
250
|
+
|
|
251
|
+
Stream live events from the gateway via SSE.
|
|
252
|
+
|
|
253
|
+
Options:
|
|
254
|
+
--agent <id> Filter events by agent ID
|
|
255
|
+
--run <id> Filter events by run ID
|
|
256
|
+
--json Output raw JSON per event
|
|
257
|
+
|
|
258
|
+
Examples:
|
|
259
|
+
moxxy events tail
|
|
260
|
+
moxxy events tail --agent <agent-id>
|
|
261
|
+
moxxy events tail --agent <agent-id> --json`,
|
|
262
|
+
|
|
263
|
+
gateway: `Usage: moxxy gateway <action>
|
|
264
|
+
|
|
265
|
+
Manage the Moxxy gateway process.
|
|
266
|
+
|
|
267
|
+
Actions:
|
|
268
|
+
start Start the gateway (launchd on macOS, systemd on Linux, fallback elsewhere)
|
|
269
|
+
stop Stop the gateway
|
|
270
|
+
restart Restart the gateway
|
|
271
|
+
status Show gateway status and health check
|
|
272
|
+
logs Tail gateway log output
|
|
273
|
+
|
|
274
|
+
Examples:
|
|
275
|
+
moxxy gateway start
|
|
276
|
+
moxxy gateway status
|
|
277
|
+
moxxy gateway logs
|
|
278
|
+
moxxy gateway restart
|
|
279
|
+
moxxy gateway stop`,
|
|
280
|
+
|
|
281
|
+
doctor: `Usage: moxxy doctor
|
|
282
|
+
|
|
283
|
+
Diagnose the Moxxy installation. Checks:
|
|
284
|
+
- Moxxy home directory (~/.moxxy)
|
|
285
|
+
- Environment variables (MOXXY_TOKEN, MOXXY_API_URL)
|
|
286
|
+
- Gateway connectivity and health
|
|
287
|
+
- Authentication
|
|
288
|
+
- Installed providers and agents
|
|
289
|
+
- Bun runtime (>= 1.2.0), Rust toolchain, Git, Chrome
|
|
290
|
+
- Provider API keys`,
|
|
291
|
+
|
|
292
|
+
update: `Usage: moxxy update [options]
|
|
293
|
+
|
|
294
|
+
Check for and install updates to the gateway binary and CLI.
|
|
295
|
+
|
|
296
|
+
Options:
|
|
297
|
+
--check Check for updates without installing
|
|
298
|
+
--force Force update even if already up to date
|
|
299
|
+
--rollback Restore previous gateway binary from backup
|
|
300
|
+
--json Output as JSON
|
|
301
|
+
|
|
302
|
+
Examples:
|
|
303
|
+
moxxy update --check
|
|
304
|
+
moxxy update
|
|
305
|
+
moxxy update --force
|
|
306
|
+
moxxy update --rollback`,
|
|
307
|
+
|
|
308
|
+
uninstall: `Usage: moxxy uninstall
|
|
309
|
+
|
|
310
|
+
Remove all Moxxy data from the system. This includes:
|
|
311
|
+
- ~/.moxxy directory (database, agents, config)
|
|
312
|
+
- Stops the gateway if running
|
|
313
|
+
|
|
314
|
+
Does NOT remove the CLI package itself. To fully remove:
|
|
315
|
+
npm remove -g @moxxy/cli`,
|
|
316
|
+
|
|
317
|
+
mcp: `Usage: moxxy mcp <action> [options]
|
|
318
|
+
|
|
319
|
+
Manage MCP (Model Context Protocol) servers for agents.
|
|
320
|
+
|
|
321
|
+
Actions:
|
|
322
|
+
list List MCP servers for an agent
|
|
323
|
+
add Register a new MCP server
|
|
324
|
+
remove Remove an MCP server
|
|
325
|
+
test Test connectivity to an MCP server
|
|
326
|
+
|
|
327
|
+
Options:
|
|
328
|
+
--agent <name> Agent name
|
|
329
|
+
--id <id> MCP server ID
|
|
330
|
+
--transport <type> Transport type: stdio or sse (add only)
|
|
331
|
+
--command <cmd> Command to run (stdio transport)
|
|
332
|
+
--args <arg> Command arguments (stdio, repeatable)
|
|
333
|
+
--url <url> Server URL (sse transport)
|
|
334
|
+
|
|
335
|
+
Examples:
|
|
336
|
+
moxxy mcp list --agent my-agent
|
|
337
|
+
moxxy mcp add --agent my-agent --id fs-server --transport stdio --command npx --args -y --args @modelcontextprotocol/server-filesystem --args /tmp
|
|
338
|
+
moxxy mcp add --agent my-agent --id remote --transport sse --url http://localhost:8080/sse
|
|
339
|
+
moxxy mcp remove --agent my-agent --id fs-server
|
|
340
|
+
moxxy mcp test --agent my-agent --id fs-server`,
|
|
341
|
+
|
|
342
|
+
plugin: `Usage: moxxy plugin <action> [options]
|
|
343
|
+
|
|
344
|
+
Manage CLI plugins that extend Moxxy functionality.
|
|
345
|
+
|
|
346
|
+
Actions:
|
|
347
|
+
list List installed plugins
|
|
348
|
+
install <package> Install a plugin from npm
|
|
349
|
+
start <name> Start a plugin
|
|
350
|
+
stop <name> Stop a plugin
|
|
351
|
+
restart <name> Restart a plugin
|
|
352
|
+
update <name> Update a plugin to the latest version
|
|
353
|
+
enable <name> Enable auto-start
|
|
354
|
+
disable <name> Disable auto-start
|
|
355
|
+
uninstall <name> Remove a plugin
|
|
356
|
+
logs <name> Tail plugin logs
|
|
357
|
+
|
|
358
|
+
Built-in plugins:
|
|
359
|
+
@moxxy/web-plugin Web Dashboard (browser UI)
|
|
360
|
+
@moxxy/virtual-office-plugin Virtual Office
|
|
361
|
+
|
|
362
|
+
Examples:
|
|
363
|
+
moxxy plugin install @moxxy/web-plugin
|
|
364
|
+
moxxy plugin start @moxxy/web-plugin
|
|
365
|
+
moxxy plugin list
|
|
366
|
+
moxxy plugin logs @moxxy/web-plugin
|
|
367
|
+
moxxy plugin uninstall my-custom-plugin`,
|
|
368
|
+
|
|
369
|
+
tui: `Usage: moxxy tui [options]
|
|
370
|
+
moxxy chat [options]
|
|
371
|
+
|
|
372
|
+
Full-screen terminal chat interface.
|
|
373
|
+
|
|
374
|
+
Options:
|
|
375
|
+
--agent <id> Start with a specific agent pre-selected
|
|
376
|
+
|
|
377
|
+
Keyboard shortcuts:
|
|
378
|
+
Enter Send message
|
|
379
|
+
Ctrl+X Stop running agent
|
|
380
|
+
/help Show available slash commands
|
|
381
|
+
/exit Exit the TUI`,
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Show styled help for a command using @clack/prompts.
|
|
386
|
+
* Falls back to console.log if p is not available.
|
|
387
|
+
*/
|
|
388
|
+
export function showHelp(commandName, p) {
|
|
389
|
+
const text = COMMAND_HELP[commandName];
|
|
390
|
+
if (!text) return;
|
|
391
|
+
p.note(text, `moxxy ${commandName}`);
|
|
392
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { getMoxxyHome } from '../commands/init.js';
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
|
|
5
|
+
const DEFAULT_REGISTRY = { version: 1, plugins: {} };
|
|
6
|
+
|
|
7
|
+
export function pluginPaths() {
|
|
8
|
+
const home = getMoxxyHome();
|
|
9
|
+
const pluginsDir = join(home, 'plugins');
|
|
10
|
+
const registryFile = join(pluginsDir, 'registry.json');
|
|
11
|
+
const logsDir = join(pluginsDir, 'logs');
|
|
12
|
+
const packageJsonFile = join(pluginsDir, 'package.json');
|
|
13
|
+
return { pluginsDir, registryFile, logsDir, packageJsonFile };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function readRegistry() {
|
|
17
|
+
const { registryFile } = pluginPaths();
|
|
18
|
+
if (!existsSync(registryFile)) return { ...DEFAULT_REGISTRY, plugins: {} };
|
|
19
|
+
try {
|
|
20
|
+
return JSON.parse(readFileSync(registryFile, 'utf-8'));
|
|
21
|
+
} catch {
|
|
22
|
+
return { ...DEFAULT_REGISTRY, plugins: {} };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function writeRegistry(registry) {
|
|
27
|
+
const { registryFile } = pluginPaths();
|
|
28
|
+
writeFileSync(registryFile, JSON.stringify(registry, null, 2));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function ensurePluginsDir() {
|
|
32
|
+
const { pluginsDir, logsDir, packageJsonFile } = pluginPaths();
|
|
33
|
+
mkdirSync(pluginsDir, { recursive: true });
|
|
34
|
+
mkdirSync(logsDir, { recursive: true });
|
|
35
|
+
if (!existsSync(packageJsonFile)) {
|
|
36
|
+
writeFileSync(packageJsonFile, JSON.stringify({ private: true, dependencies: {} }, null, 2));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function readPluginMeta(pluginName) {
|
|
41
|
+
const { pluginsDir } = pluginPaths();
|
|
42
|
+
const pkgPath = join(pluginsDir, 'node_modules', pluginName, 'package.json');
|
|
43
|
+
if (!existsSync(pkgPath)) return null;
|
|
44
|
+
try {
|
|
45
|
+
return JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
46
|
+
} catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function validatePluginMeta(meta) {
|
|
52
|
+
const errors = [];
|
|
53
|
+
if (!meta) {
|
|
54
|
+
return { valid: false, errors: ['Plugin package.json not found'] };
|
|
55
|
+
}
|
|
56
|
+
if (!meta.moxxy || meta.moxxy.type !== 'plugin') {
|
|
57
|
+
errors.push('Missing or invalid moxxy.type (must be "plugin")');
|
|
58
|
+
}
|
|
59
|
+
if (!meta.scripts || !meta.scripts['plugin:start']) {
|
|
60
|
+
errors.push('Missing required script "plugin:start"');
|
|
61
|
+
}
|
|
62
|
+
return { valid: errors.length === 0, errors };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function isProcessAlive(pid) {
|
|
66
|
+
try {
|
|
67
|
+
process.kill(pid, 0);
|
|
68
|
+
return true;
|
|
69
|
+
} catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function sanitizeLogFileName(pluginName) {
|
|
75
|
+
return pluginName.replace(/\//g, '--') + '.log';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function buildPluginEnv(pluginName, port) {
|
|
79
|
+
const apiUrl = process.env.MOXXY_API_URL || 'http://localhost:3000';
|
|
80
|
+
const token = process.env.MOXXY_TOKEN || '';
|
|
81
|
+
return {
|
|
82
|
+
...process.env,
|
|
83
|
+
MOXXY_API_URL: apiUrl,
|
|
84
|
+
MOXXY_TOKEN: token,
|
|
85
|
+
MOXXY_PLUGIN_NAME: pluginName,
|
|
86
|
+
MOXXY_PLUGIN_PORT: port ? String(port) : '',
|
|
87
|
+
MOXXY_HOME: getMoxxyHome(),
|
|
88
|
+
PORT: port ? String(port) : '',
|
|
89
|
+
// Vite exposes only VITE_-prefixed vars to browser code
|
|
90
|
+
VITE_MOXXY_API_URL: apiUrl,
|
|
91
|
+
VITE_MOXXY_TOKEN: token,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const BUILTIN_PLUGINS = [
|
|
96
|
+
{ name: '@moxxy/web-plugin', label: 'Web Dashboard', hint: 'browser-based dashboard', defaultPort: 17900 },
|
|
97
|
+
{ name: '@moxxy/virtual-office-plugin', label: 'Virtual Office', hint: 'virtual office environment', defaultPort: 17901 },
|
|
98
|
+
];
|