@moxxy/cli 1.4.0 → 1.5.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/commands/channel.js +88 -25
- package/src/commands/init.js +128 -65
package/package.json
CHANGED
package/src/commands/channel.js
CHANGED
|
@@ -49,16 +49,12 @@ async function createChannel(client, args) {
|
|
|
49
49
|
message: 'Channel type',
|
|
50
50
|
options: [
|
|
51
51
|
{ value: 'telegram', label: 'Telegram', hint: 'BotFather bot token required' },
|
|
52
|
-
{ value: 'discord', label: 'Discord', hint: '
|
|
52
|
+
{ value: 'discord', label: 'Discord', hint: 'Discord bot token required' },
|
|
53
|
+
{ value: 'whatsapp', label: 'WhatsApp', hint: 'WhatsApp Business API token required' },
|
|
53
54
|
],
|
|
54
55
|
});
|
|
55
56
|
handleCancel(channelType);
|
|
56
57
|
|
|
57
|
-
if (channelType === 'discord') {
|
|
58
|
-
p.log.info('Discord channel support is coming soon.');
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
58
|
// Step 1: Select agent to bind
|
|
63
59
|
let agentId;
|
|
64
60
|
try {
|
|
@@ -80,33 +76,84 @@ async function createChannel(client, args) {
|
|
|
80
76
|
return;
|
|
81
77
|
}
|
|
82
78
|
|
|
83
|
-
// Step 2: Get
|
|
84
|
-
|
|
85
|
-
'1. Open Telegram and talk to @BotFather\n' +
|
|
86
|
-
'2. Send /newbot and follow the prompts\n' +
|
|
87
|
-
'3. Copy the bot token',
|
|
88
|
-
'Telegram Bot Setup'
|
|
89
|
-
);
|
|
79
|
+
// Step 2: Get credentials based on channel type
|
|
80
|
+
let botToken, displayName, config;
|
|
90
81
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
82
|
+
if (channelType === 'telegram') {
|
|
83
|
+
p.note(
|
|
84
|
+
'1. Open Telegram and talk to @BotFather\n' +
|
|
85
|
+
'2. Send /newbot and follow the prompts\n' +
|
|
86
|
+
'3. Copy the bot token',
|
|
87
|
+
'Telegram Bot Setup'
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
botToken = await p.password({
|
|
91
|
+
message: 'Paste your Telegram bot token',
|
|
92
|
+
});
|
|
93
|
+
handleCancel(botToken);
|
|
94
|
+
} else if (channelType === 'discord') {
|
|
95
|
+
p.note(
|
|
96
|
+
'1. Go to https://discord.com/developers/applications\n' +
|
|
97
|
+
'2. Create a new application → Bot → copy the bot token\n' +
|
|
98
|
+
'3. Enable MESSAGE CONTENT intent under Bot → Privileged Intents\n' +
|
|
99
|
+
'4. Invite the bot to your server with the Messages scope',
|
|
100
|
+
'Discord Bot Setup'
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
botToken = await p.password({
|
|
104
|
+
message: 'Paste your Discord bot token',
|
|
105
|
+
});
|
|
106
|
+
handleCancel(botToken);
|
|
107
|
+
} else if (channelType === 'whatsapp') {
|
|
108
|
+
p.note(
|
|
109
|
+
'1. Go to https://developers.facebook.com and create an app\n' +
|
|
110
|
+
'2. Add the WhatsApp product to your app\n' +
|
|
111
|
+
'3. Copy the permanent access token and Phone Number ID\n' +
|
|
112
|
+
'4. Configure the webhook URL to: <your-moxxy-url>/v1/channels/whatsapp/webhook',
|
|
113
|
+
'WhatsApp Business API Setup'
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
botToken = await p.password({
|
|
117
|
+
message: 'Paste your WhatsApp access token',
|
|
118
|
+
});
|
|
119
|
+
handleCancel(botToken);
|
|
120
|
+
|
|
121
|
+
const phoneNumberId = await p.text({
|
|
122
|
+
message: 'Phone Number ID (from WhatsApp Business API)',
|
|
123
|
+
});
|
|
124
|
+
handleCancel(phoneNumberId);
|
|
125
|
+
|
|
126
|
+
const verifyToken = await p.text({
|
|
127
|
+
message: 'Webhook verify token (you choose this, used to verify the webhook)',
|
|
128
|
+
placeholder: 'my-verify-token',
|
|
129
|
+
});
|
|
130
|
+
handleCancel(verifyToken);
|
|
95
131
|
|
|
96
|
-
|
|
132
|
+
config = {
|
|
133
|
+
phone_number_id: phoneNumberId,
|
|
134
|
+
verify_token: verifyToken || undefined,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
displayName = await p.text({
|
|
97
139
|
message: 'Display name for this channel',
|
|
98
|
-
placeholder: 'My
|
|
140
|
+
placeholder: channelType === 'telegram' ? 'My Telegram Bot' :
|
|
141
|
+
channelType === 'discord' ? 'My Discord Bot' : 'My WhatsApp Bot',
|
|
99
142
|
});
|
|
100
143
|
handleCancel(displayName);
|
|
101
144
|
|
|
145
|
+
const defaultName = channelType === 'telegram' ? 'Telegram Bot' :
|
|
146
|
+
channelType === 'discord' ? 'Discord Bot' : 'WhatsApp Bot';
|
|
147
|
+
|
|
102
148
|
// Step 3: Create channel
|
|
103
149
|
let channel;
|
|
104
150
|
try {
|
|
105
151
|
channel = await withSpinner('Creating channel...', () =>
|
|
106
152
|
client.request('/v1/channels', 'POST', {
|
|
107
153
|
channel_type: channelType,
|
|
108
|
-
display_name: displayName ||
|
|
154
|
+
display_name: displayName || defaultName,
|
|
109
155
|
bot_token: botToken,
|
|
156
|
+
...(config ? { config } : {}),
|
|
110
157
|
}), 'Channel created.');
|
|
111
158
|
|
|
112
159
|
showResult('Channel Created', {
|
|
@@ -121,11 +168,27 @@ async function createChannel(client, args) {
|
|
|
121
168
|
}
|
|
122
169
|
|
|
123
170
|
// Step 4: Wait for pairing code
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
171
|
+
if (channelType === 'telegram') {
|
|
172
|
+
p.note(
|
|
173
|
+
'1. Open your Telegram bot and send /start\n' +
|
|
174
|
+
'2. Copy the 6-digit pairing code',
|
|
175
|
+
'Pair your chat'
|
|
176
|
+
);
|
|
177
|
+
} else if (channelType === 'discord') {
|
|
178
|
+
p.note(
|
|
179
|
+
'1. Send a message to your Discord bot or in a channel it can see\n' +
|
|
180
|
+
'2. The bot will respond with a pairing code if not yet paired\n' +
|
|
181
|
+
'3. Copy the 6-digit pairing code',
|
|
182
|
+
'Pair your chat'
|
|
183
|
+
);
|
|
184
|
+
} else if (channelType === 'whatsapp') {
|
|
185
|
+
p.note(
|
|
186
|
+
'1. Send a message to your WhatsApp number\n' +
|
|
187
|
+
'2. The bot will respond with a pairing code\n' +
|
|
188
|
+
'3. Copy the 6-digit pairing code',
|
|
189
|
+
'Pair your chat'
|
|
190
|
+
);
|
|
191
|
+
}
|
|
129
192
|
|
|
130
193
|
const code = await p.text({
|
|
131
194
|
message: 'Enter 6-digit pairing code',
|
package/src/commands/init.js
CHANGED
|
@@ -650,12 +650,12 @@ export async function runInit(client, args) {
|
|
|
650
650
|
|
|
651
651
|
// Step 6: Channel setup (optional)
|
|
652
652
|
p.note(
|
|
653
|
-
'Channels enable agent communication via Telegram or
|
|
653
|
+
'Channels enable agent communication via Telegram, Discord, or WhatsApp.\n' +
|
|
654
654
|
'You can set up channels later with: moxxy channel create',
|
|
655
655
|
'Channels'
|
|
656
656
|
);
|
|
657
657
|
const setupChannel = await p.confirm({
|
|
658
|
-
message: 'Set up a messaging channel
|
|
658
|
+
message: 'Set up a messaging channel?',
|
|
659
659
|
initialValue: false,
|
|
660
660
|
});
|
|
661
661
|
handleCancel(setupChannel);
|
|
@@ -665,11 +665,14 @@ export async function runInit(client, args) {
|
|
|
665
665
|
message: 'Channel type',
|
|
666
666
|
options: [
|
|
667
667
|
{ value: 'telegram', label: 'Telegram', hint: 'BotFather bot token required' },
|
|
668
|
-
{ value: 'discord', label: 'Discord', hint: '
|
|
668
|
+
{ value: 'discord', label: 'Discord', hint: 'Discord bot token required' },
|
|
669
|
+
{ value: 'whatsapp', label: 'WhatsApp', hint: 'WhatsApp Business API token required' },
|
|
669
670
|
],
|
|
670
671
|
});
|
|
671
672
|
handleCancel(channelType);
|
|
672
673
|
|
|
674
|
+
let botToken, displayName, channelConfig;
|
|
675
|
+
|
|
673
676
|
if (channelType === 'telegram') {
|
|
674
677
|
p.note(
|
|
675
678
|
'1. Open Telegram and talk to @BotFather\n' +
|
|
@@ -678,88 +681,148 @@ export async function runInit(client, args) {
|
|
|
678
681
|
'Telegram Bot Setup'
|
|
679
682
|
);
|
|
680
683
|
|
|
681
|
-
|
|
684
|
+
botToken = await p.password({
|
|
682
685
|
message: 'Paste your Telegram bot token',
|
|
683
686
|
});
|
|
684
687
|
handleCancel(botToken);
|
|
688
|
+
} else if (channelType === 'discord') {
|
|
689
|
+
p.note(
|
|
690
|
+
'1. Go to https://discord.com/developers/applications\n' +
|
|
691
|
+
'2. Create a new application → Bot → copy the bot token\n' +
|
|
692
|
+
'3. Enable MESSAGE CONTENT intent under Bot → Privileged Intents\n' +
|
|
693
|
+
'4. Invite the bot to your server with the Messages scope',
|
|
694
|
+
'Discord Bot Setup'
|
|
695
|
+
);
|
|
685
696
|
|
|
686
|
-
|
|
687
|
-
message: '
|
|
688
|
-
placeholder: 'My Moxxy Bot',
|
|
697
|
+
botToken = await p.password({
|
|
698
|
+
message: 'Paste your Discord bot token',
|
|
689
699
|
});
|
|
690
|
-
handleCancel(
|
|
700
|
+
handleCancel(botToken);
|
|
701
|
+
} else if (channelType === 'whatsapp') {
|
|
702
|
+
p.note(
|
|
703
|
+
'1. Go to https://developers.facebook.com and create an app\n' +
|
|
704
|
+
'2. Add the WhatsApp product to your app\n' +
|
|
705
|
+
'3. Copy the permanent access token and Phone Number ID\n' +
|
|
706
|
+
'4. Configure the webhook URL to: <your-moxxy-url>/v1/channels/whatsapp/webhook',
|
|
707
|
+
'WhatsApp Business API Setup'
|
|
708
|
+
);
|
|
691
709
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
710
|
+
botToken = await p.password({
|
|
711
|
+
message: 'Paste your WhatsApp access token',
|
|
712
|
+
});
|
|
713
|
+
handleCancel(botToken);
|
|
714
|
+
|
|
715
|
+
const phoneNumberId = await p.text({
|
|
716
|
+
message: 'Phone Number ID (from WhatsApp Business API)',
|
|
717
|
+
});
|
|
718
|
+
handleCancel(phoneNumberId);
|
|
699
719
|
|
|
700
|
-
|
|
720
|
+
const verifyToken = await p.text({
|
|
721
|
+
message: 'Webhook verify token (you choose this, used to verify the webhook)',
|
|
722
|
+
placeholder: 'my-verify-token',
|
|
723
|
+
});
|
|
724
|
+
handleCancel(verifyToken);
|
|
725
|
+
|
|
726
|
+
channelConfig = {
|
|
727
|
+
phone_number_id: phoneNumberId,
|
|
728
|
+
verify_token: verifyToken || undefined,
|
|
729
|
+
};
|
|
730
|
+
}
|
|
701
731
|
|
|
702
|
-
|
|
732
|
+
const defaultName = channelType === 'telegram' ? 'Telegram Bot' :
|
|
733
|
+
channelType === 'discord' ? 'Discord Bot' : 'WhatsApp Bot';
|
|
734
|
+
const channelLabel = channelType === 'telegram' ? 'Telegram' :
|
|
735
|
+
channelType === 'discord' ? 'Discord' : 'WhatsApp';
|
|
736
|
+
|
|
737
|
+
displayName = await p.text({
|
|
738
|
+
message: 'Display name for this channel',
|
|
739
|
+
placeholder: `My ${channelLabel} Bot`,
|
|
740
|
+
});
|
|
741
|
+
handleCancel(displayName);
|
|
742
|
+
|
|
743
|
+
try {
|
|
744
|
+
const result = await withSpinner(`Registering ${channelLabel} channel...`, () =>
|
|
745
|
+
client.request('/v1/channels', 'POST', {
|
|
746
|
+
channel_type: channelType,
|
|
747
|
+
display_name: displayName || defaultName,
|
|
748
|
+
bot_token: botToken,
|
|
749
|
+
...(channelConfig ? { config: channelConfig } : {}),
|
|
750
|
+
}), 'Channel registered.');
|
|
751
|
+
|
|
752
|
+
showResult(`${channelLabel} Channel`, { ID: result.id, Status: result.status });
|
|
753
|
+
|
|
754
|
+
// Interactive pairing
|
|
755
|
+
if (channelType === 'telegram') {
|
|
703
756
|
p.note(
|
|
704
757
|
'1. Open your Telegram bot and send /start\n' +
|
|
705
758
|
'2. You will receive a 6-digit pairing code',
|
|
706
759
|
'Pair your chat'
|
|
707
760
|
);
|
|
761
|
+
} else if (channelType === 'discord') {
|
|
762
|
+
p.note(
|
|
763
|
+
'1. Send a message to your Discord bot or in a channel it can see\n' +
|
|
764
|
+
'2. Type /start to receive a 6-digit pairing code',
|
|
765
|
+
'Pair your chat'
|
|
766
|
+
);
|
|
767
|
+
} else if (channelType === 'whatsapp') {
|
|
768
|
+
p.note(
|
|
769
|
+
'1. Send /start to your WhatsApp number\n' +
|
|
770
|
+
'2. You will receive a 6-digit pairing code',
|
|
771
|
+
'Pair your chat'
|
|
772
|
+
);
|
|
773
|
+
}
|
|
708
774
|
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
775
|
+
const pairCode = await p.text({
|
|
776
|
+
message: 'Enter the 6-digit pairing code',
|
|
777
|
+
placeholder: '123456',
|
|
778
|
+
validate: (v) => {
|
|
779
|
+
if (!v || v.trim().length === 0) return 'Code is required';
|
|
780
|
+
},
|
|
781
|
+
});
|
|
782
|
+
handleCancel(pairCode);
|
|
717
783
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
p.log.info(`Pair later with: moxxy channel pair --code ${pairCode} --agent <agent-id>`);
|
|
726
|
-
} else {
|
|
727
|
-
agentId = await p.select({
|
|
728
|
-
message: 'Select agent to bind',
|
|
729
|
-
options: agents.map(a => ({
|
|
730
|
-
value: a.name,
|
|
731
|
-
label: `${a.name} (${a.provider_id}/${a.model_id})`,
|
|
732
|
-
})),
|
|
733
|
-
});
|
|
734
|
-
handleCancel(agentId);
|
|
735
|
-
}
|
|
736
|
-
} catch (err) {
|
|
737
|
-
p.log.warn(`Could not list agents: ${err.message}`);
|
|
784
|
+
// Pick an agent to bind
|
|
785
|
+
let agentId;
|
|
786
|
+
try {
|
|
787
|
+
const agents = await withSpinner('Fetching agents...', () =>
|
|
788
|
+
client.listAgents(), 'Agents loaded.');
|
|
789
|
+
if (!agents || agents.length === 0) {
|
|
790
|
+
p.log.warn('No agents found. Create one first with: moxxy agent create');
|
|
738
791
|
p.log.info(`Pair later with: moxxy channel pair --code ${pairCode} --agent <agent-id>`);
|
|
792
|
+
} else {
|
|
793
|
+
agentId = await p.select({
|
|
794
|
+
message: 'Select agent to bind',
|
|
795
|
+
options: agents.map(a => ({
|
|
796
|
+
value: a.name,
|
|
797
|
+
label: `${a.name} (${a.provider_id}/${a.model_id})`,
|
|
798
|
+
})),
|
|
799
|
+
});
|
|
800
|
+
handleCancel(agentId);
|
|
739
801
|
}
|
|
802
|
+
} catch (err) {
|
|
803
|
+
p.log.warn(`Could not list agents: ${err.message}`);
|
|
804
|
+
p.log.info(`Pair later with: moxxy channel pair --code ${pairCode} --agent <agent-id>`);
|
|
805
|
+
}
|
|
740
806
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
}
|
|
807
|
+
if (agentId) {
|
|
808
|
+
try {
|
|
809
|
+
const pairResult = await withSpinner('Pairing...', () =>
|
|
810
|
+
client.request(`/v1/channels/${result.id}/pair`, 'POST', {
|
|
811
|
+
code: pairCode,
|
|
812
|
+
agent_id: agentId,
|
|
813
|
+
}), 'Paired successfully.');
|
|
814
|
+
showResult('Channel Paired', {
|
|
815
|
+
'Binding ID': pairResult.id,
|
|
816
|
+
Agent: pairResult.agent_id,
|
|
817
|
+
'External Chat': pairResult.external_chat_id,
|
|
818
|
+
});
|
|
819
|
+
} catch (err) {
|
|
820
|
+
p.log.error(`Failed to pair: ${err.message}`);
|
|
821
|
+
p.log.info(`Try again with: moxxy channel pair --code ${pairCode} --agent ${agentId}`);
|
|
757
822
|
}
|
|
758
|
-
} catch (err) {
|
|
759
|
-
p.log.error(`Failed to register channel: ${err.message}`);
|
|
760
823
|
}
|
|
761
|
-
}
|
|
762
|
-
p.log.
|
|
824
|
+
} catch (err) {
|
|
825
|
+
p.log.error(`Failed to register channel: ${err.message}`);
|
|
763
826
|
}
|
|
764
827
|
}
|
|
765
828
|
|