agent-messenger 2.0.0 → 2.1.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/.claude-plugin/marketplace.json +14 -1
- package/.claude-plugin/plugin.json +4 -2
- package/README.md +33 -29
- package/dist/package.json +10 -2
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +3 -0
- package/dist/src/cli.js.map +1 -1
- package/dist/src/platforms/webex/app-config.d.ts +7 -0
- package/dist/src/platforms/webex/app-config.d.ts.map +1 -0
- package/dist/src/platforms/webex/app-config.js +20 -0
- package/dist/src/platforms/webex/app-config.js.map +1 -0
- package/dist/src/platforms/webex/cli.d.ts +5 -0
- package/dist/src/platforms/webex/cli.d.ts.map +1 -0
- package/dist/src/platforms/webex/cli.js +32 -0
- package/dist/src/platforms/webex/cli.js.map +1 -0
- package/dist/src/platforms/webex/client.d.ts +45 -0
- package/dist/src/platforms/webex/client.d.ts.map +1 -0
- package/dist/src/platforms/webex/client.js +175 -0
- package/dist/src/platforms/webex/client.js.map +1 -0
- package/dist/src/platforms/webex/commands/auth.d.ts +15 -0
- package/dist/src/platforms/webex/commands/auth.d.ts.map +1 -0
- package/dist/src/platforms/webex/commands/auth.js +124 -0
- package/dist/src/platforms/webex/commands/auth.js.map +1 -0
- package/dist/src/platforms/webex/commands/index.d.ts +6 -0
- package/dist/src/platforms/webex/commands/index.d.ts.map +1 -0
- package/dist/src/platforms/webex/commands/index.js +6 -0
- package/dist/src/platforms/webex/commands/index.js.map +1 -0
- package/dist/src/platforms/webex/commands/member.d.ts +7 -0
- package/dist/src/platforms/webex/commands/member.d.ts.map +1 -0
- package/dist/src/platforms/webex/commands/member.js +34 -0
- package/dist/src/platforms/webex/commands/member.js.map +1 -0
- package/dist/src/platforms/webex/commands/message.d.ts +26 -0
- package/dist/src/platforms/webex/commands/message.d.ts.map +1 -0
- package/dist/src/platforms/webex/commands/message.js +153 -0
- package/dist/src/platforms/webex/commands/message.js.map +1 -0
- package/dist/src/platforms/webex/commands/snapshot.d.ts +9 -0
- package/dist/src/platforms/webex/commands/snapshot.d.ts.map +1 -0
- package/dist/src/platforms/webex/commands/snapshot.js +72 -0
- package/dist/src/platforms/webex/commands/snapshot.js.map +1 -0
- package/dist/src/platforms/webex/commands/space.d.ts +11 -0
- package/dist/src/platforms/webex/commands/space.d.ts.map +1 -0
- package/dist/src/platforms/webex/commands/space.js +59 -0
- package/dist/src/platforms/webex/commands/space.js.map +1 -0
- package/dist/src/platforms/webex/credential-manager.d.ts +23 -0
- package/dist/src/platforms/webex/credential-manager.d.ts.map +1 -0
- package/dist/src/platforms/webex/credential-manager.js +148 -0
- package/dist/src/platforms/webex/credential-manager.js.map +1 -0
- package/dist/src/platforms/webex/ensure-auth.d.ts +2 -0
- package/dist/src/platforms/webex/ensure-auth.d.ts.map +1 -0
- package/dist/src/platforms/webex/ensure-auth.js +20 -0
- package/dist/src/platforms/webex/ensure-auth.js.map +1 -0
- package/dist/src/platforms/webex/index.d.ts +6 -0
- package/dist/src/platforms/webex/index.d.ts.map +1 -0
- package/dist/src/platforms/webex/index.js +5 -0
- package/dist/src/platforms/webex/index.js.map +1 -0
- package/dist/src/platforms/webex/types.d.ts +124 -0
- package/dist/src/platforms/webex/types.d.ts.map +1 -0
- package/dist/src/platforms/webex/types.js +63 -0
- package/dist/src/platforms/webex/types.js.map +1 -0
- package/dist/src/tui/adapters/webex-adapter.d.ts +14 -0
- package/dist/src/tui/adapters/webex-adapter.d.ts.map +1 -0
- package/dist/src/tui/adapters/webex-adapter.js +79 -0
- package/dist/src/tui/adapters/webex-adapter.js.map +1 -0
- package/dist/src/tui/app.d.ts.map +1 -1
- package/dist/src/tui/app.js +2 -0
- package/dist/src/tui/app.js.map +1 -1
- package/docs/content/docs/cli/meta.json +1 -0
- package/docs/content/docs/cli/webex.mdx +291 -0
- package/docs/content/docs/sdk/meta.json +1 -1
- package/docs/content/docs/sdk/webex.mdx +260 -0
- package/docs/content/docs/tui.mdx +4 -3
- package/docs/src/app/page.tsx +2 -2
- package/package.json +10 -2
- package/skills/agent-channeltalk/SKILL.md +1 -1
- package/skills/agent-channeltalkbot/SKILL.md +1 -1
- package/skills/agent-discord/SKILL.md +1 -1
- package/skills/agent-discordbot/SKILL.md +1 -1
- package/skills/agent-instagram/SKILL.md +1 -1
- package/skills/agent-kakaotalk/SKILL.md +1 -1
- package/skills/agent-line/SKILL.md +1 -1
- package/skills/agent-slack/SKILL.md +1 -1
- package/skills/agent-slackbot/SKILL.md +1 -1
- package/skills/agent-teams/SKILL.md +1 -1
- package/skills/agent-telegram/SKILL.md +1 -1
- package/skills/agent-webex/SKILL.md +386 -0
- package/skills/agent-webex/references/authentication.md +318 -0
- package/skills/agent-webex/references/common-patterns.md +723 -0
- package/skills/agent-webex/templates/monitor-space.sh +165 -0
- package/skills/agent-webex/templates/post-message.sh +170 -0
- package/skills/agent-whatsapp/SKILL.md +1 -1
- package/skills/agent-whatsappbot/SKILL.md +1 -1
- package/src/cli.ts +4 -0
- package/src/platforms/webex/app-config.test.ts +98 -0
- package/src/platforms/webex/app-config.ts +31 -0
- package/src/platforms/webex/cli.test.ts +58 -0
- package/src/platforms/webex/cli.ts +39 -0
- package/src/platforms/webex/client.test.ts +429 -0
- package/src/platforms/webex/client.ts +247 -0
- package/src/platforms/webex/commands/auth.test.ts +222 -0
- package/src/platforms/webex/commands/auth.ts +180 -0
- package/src/platforms/webex/commands/index.ts +5 -0
- package/src/platforms/webex/commands/member.test.ts +103 -0
- package/src/platforms/webex/commands/member.ts +45 -0
- package/src/platforms/webex/commands/message.test.ts +231 -0
- package/src/platforms/webex/commands/message.ts +204 -0
- package/src/platforms/webex/commands/snapshot.test.ts +96 -0
- package/src/platforms/webex/commands/snapshot.ts +91 -0
- package/src/platforms/webex/commands/space.test.ts +206 -0
- package/src/platforms/webex/commands/space.ts +74 -0
- package/src/platforms/webex/credential-manager.test.ts +314 -0
- package/src/platforms/webex/credential-manager.ts +197 -0
- package/src/platforms/webex/ensure-auth.test.ts +85 -0
- package/src/platforms/webex/ensure-auth.ts +19 -0
- package/src/platforms/webex/index.test.ts +25 -0
- package/src/platforms/webex/index.ts +17 -0
- package/src/platforms/webex/types.test.ts +307 -0
- package/src/platforms/webex/types.ts +127 -0
- package/src/tui/adapters/webex-adapter.ts +103 -0
- package/src/tui/app.ts +2 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# monitor-space.sh - Monitor a Webex space for new messages
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# ./monitor-space.sh <space-id> [interval]
|
|
7
|
+
#
|
|
8
|
+
# Arguments:
|
|
9
|
+
# space-id - Space ID to monitor (use 'space list' to find IDs)
|
|
10
|
+
# interval - Polling interval in seconds (default: 10)
|
|
11
|
+
#
|
|
12
|
+
# Example:
|
|
13
|
+
# ./monitor-space.sh "Y2lzY29zcGFyazovL..."
|
|
14
|
+
# ./monitor-space.sh "Y2lzY29zcGFyazovL..." 5
|
|
15
|
+
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
|
|
18
|
+
if [ $# -lt 1 ]; then
|
|
19
|
+
echo "Usage: $0 <space-id> [interval]"
|
|
20
|
+
echo ""
|
|
21
|
+
echo "Examples:"
|
|
22
|
+
echo " $0 'Y2lzY29zcGFyazovL...' # Monitor space, poll every 10s"
|
|
23
|
+
echo " $0 'Y2lzY29zcGFyazovL...' 5 # Monitor space, poll every 5s"
|
|
24
|
+
echo ""
|
|
25
|
+
echo "To find space IDs, run: agent-webex space list"
|
|
26
|
+
exit 1
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
SPACE_ID="$1"
|
|
30
|
+
INTERVAL="${2:-10}"
|
|
31
|
+
|
|
32
|
+
RED='\033[0;31m'
|
|
33
|
+
GREEN='\033[0;32m'
|
|
34
|
+
YELLOW='\033[1;33m'
|
|
35
|
+
BLUE='\033[0;34m'
|
|
36
|
+
NC='\033[0m'
|
|
37
|
+
|
|
38
|
+
LAST_ID=""
|
|
39
|
+
FIRST_RUN=true
|
|
40
|
+
|
|
41
|
+
format_time() {
|
|
42
|
+
local ts=$1
|
|
43
|
+
if command -v gdate &> /dev/null; then
|
|
44
|
+
gdate -d "$ts" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || echo "$ts"
|
|
45
|
+
else
|
|
46
|
+
date -d "$ts" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || echo "$ts"
|
|
47
|
+
fi
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
truncate_text() {
|
|
51
|
+
local text=$1
|
|
52
|
+
local max_length=100
|
|
53
|
+
|
|
54
|
+
if [ ${#text} -gt $max_length ]; then
|
|
55
|
+
echo "${text:0:$max_length}..."
|
|
56
|
+
else
|
|
57
|
+
echo "$text"
|
|
58
|
+
fi
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
check_messages() {
|
|
62
|
+
MESSAGES=$(agent-webex message list "$SPACE_ID" --limit 1 2>&1)
|
|
63
|
+
|
|
64
|
+
if echo "$MESSAGES" | jq -e '.error' > /dev/null 2>&1; then
|
|
65
|
+
ERROR_MSG=$(echo "$MESSAGES" | jq -r '.error // "Unknown error"')
|
|
66
|
+
echo -e "${RED}Error: $ERROR_MSG${NC}"
|
|
67
|
+
|
|
68
|
+
if echo "$ERROR_MSG" | grep -Eqi "401|unauthorized"; then
|
|
69
|
+
echo -e "${RED}Token expired or invalid. Get a new token from https://developer.webex.com${NC}"
|
|
70
|
+
exit 1
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
return 1
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
LATEST_ID=$(echo "$MESSAGES" | jq -r '.[0].id // ""')
|
|
77
|
+
|
|
78
|
+
if [ -z "$LATEST_ID" ]; then
|
|
79
|
+
if [ "$FIRST_RUN" = true ]; then
|
|
80
|
+
echo -e "${YELLOW}No messages in space yet${NC}"
|
|
81
|
+
fi
|
|
82
|
+
FIRST_RUN=false
|
|
83
|
+
return 0
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
if [ "$LATEST_ID" != "$LAST_ID" ]; then
|
|
87
|
+
if [ "$FIRST_RUN" = false ] && [ -n "$LAST_ID" ]; then
|
|
88
|
+
TEXT=$(echo "$MESSAGES" | jq -r '.[0].text // ""')
|
|
89
|
+
AUTHOR=$(echo "$MESSAGES" | jq -r '.[0].personEmail // "Unknown"')
|
|
90
|
+
TIMESTAMP=$(echo "$MESSAGES" | jq -r '.[0].created // ""')
|
|
91
|
+
|
|
92
|
+
TIME=$(format_time "$TIMESTAMP")
|
|
93
|
+
|
|
94
|
+
echo ""
|
|
95
|
+
echo -e "${GREEN}----------------------------------------------${NC}"
|
|
96
|
+
echo -e "${BLUE}New message in space${NC}"
|
|
97
|
+
echo -e "${GREEN}----------------------------------------------${NC}"
|
|
98
|
+
echo -e "Time: $TIME"
|
|
99
|
+
echo -e "From: $AUTHOR"
|
|
100
|
+
echo -e "Message: $(truncate_text "$TEXT")"
|
|
101
|
+
echo -e "${GREEN}----------------------------------------------${NC}"
|
|
102
|
+
|
|
103
|
+
# Uncomment to auto-respond to keywords:
|
|
104
|
+
# if echo "$TEXT" | grep -qi "status"; then
|
|
105
|
+
# agent-webex message send "$SPACE_ID" "All systems operational."
|
|
106
|
+
# fi
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
LAST_ID="$LATEST_ID"
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
FIRST_RUN=false
|
|
113
|
+
return 0
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if ! command -v agent-webex &> /dev/null; then
|
|
117
|
+
echo -e "${RED}Error: agent-webex not found${NC}"
|
|
118
|
+
echo ""
|
|
119
|
+
echo "Install it with:"
|
|
120
|
+
echo " npm install -g agent-messenger"
|
|
121
|
+
exit 1
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
echo "Checking authentication..."
|
|
125
|
+
AUTH_STATUS=$(agent-webex auth status 2>&1)
|
|
126
|
+
|
|
127
|
+
if echo "$AUTH_STATUS" | jq -e '.error' > /dev/null 2>&1; then
|
|
128
|
+
echo -e "${RED}Not authenticated!${NC}"
|
|
129
|
+
echo ""
|
|
130
|
+
echo "Run this to authenticate:"
|
|
131
|
+
echo " agent-webex auth login --token <token>"
|
|
132
|
+
echo ""
|
|
133
|
+
echo "Get a token at: https://developer.webex.com/docs/getting-started"
|
|
134
|
+
exit 1
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
USER_NAME=$(echo "$AUTH_STATUS" | jq -r '.displayName // "Unknown"')
|
|
138
|
+
echo -e "${GREEN}Authenticated as: $USER_NAME${NC}"
|
|
139
|
+
echo ""
|
|
140
|
+
|
|
141
|
+
echo "Verifying space..."
|
|
142
|
+
SPACE_INFO=$(agent-webex space info "$SPACE_ID" 2>&1)
|
|
143
|
+
|
|
144
|
+
if echo "$SPACE_INFO" | jq -e '.error' > /dev/null 2>&1; then
|
|
145
|
+
echo -e "${RED}Space '$SPACE_ID' not found${NC}"
|
|
146
|
+
echo ""
|
|
147
|
+
echo "List available spaces with:"
|
|
148
|
+
echo " agent-webex space list"
|
|
149
|
+
exit 1
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
SPACE_TITLE=$(echo "$SPACE_INFO" | jq -r '.title // "Unknown"')
|
|
153
|
+
echo -e "${GREEN}Monitoring: $SPACE_TITLE ($SPACE_ID)${NC}"
|
|
154
|
+
echo ""
|
|
155
|
+
|
|
156
|
+
echo -e "${YELLOW}Monitoring for new messages (polling every ${INTERVAL}s)...${NC}"
|
|
157
|
+
echo -e "${YELLOW}Press Ctrl+C to stop${NC}"
|
|
158
|
+
echo ""
|
|
159
|
+
|
|
160
|
+
trap 'echo -e "\n${YELLOW}Monitoring stopped${NC}"; exit 0' INT
|
|
161
|
+
|
|
162
|
+
while true; do
|
|
163
|
+
check_messages
|
|
164
|
+
sleep "$INTERVAL"
|
|
165
|
+
done
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# post-message.sh - Send a message to a Webex space with error handling
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# ./post-message.sh <space-id> <message>
|
|
7
|
+
# ./post-message.sh --space-title <title> <message>
|
|
8
|
+
#
|
|
9
|
+
# Example:
|
|
10
|
+
# ./post-message.sh "Y2lzY29zcGFyazovL..." "Hello from script!"
|
|
11
|
+
# ./post-message.sh --space-title Engineering "Deployment completed"
|
|
12
|
+
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
# Colors for output
|
|
16
|
+
RED='\033[0;31m'
|
|
17
|
+
GREEN='\033[0;32m'
|
|
18
|
+
YELLOW='\033[1;33m'
|
|
19
|
+
NC='\033[0m' # No Color
|
|
20
|
+
|
|
21
|
+
# Parse arguments
|
|
22
|
+
SPACE_ID=""
|
|
23
|
+
SPACE_TITLE=""
|
|
24
|
+
MESSAGE=""
|
|
25
|
+
|
|
26
|
+
if [ "$1" = "--space-title" ]; then
|
|
27
|
+
if [ $# -lt 3 ]; then
|
|
28
|
+
echo "Usage: $0 --space-title <title> <message>"
|
|
29
|
+
echo ""
|
|
30
|
+
echo "Example:"
|
|
31
|
+
echo " $0 --space-title Engineering 'Build completed'"
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
SPACE_TITLE="$2"
|
|
35
|
+
MESSAGE="$3"
|
|
36
|
+
elif [ $# -lt 2 ]; then
|
|
37
|
+
echo "Usage: $0 <space-id> <message>"
|
|
38
|
+
echo " $0 --space-title <title> <message>"
|
|
39
|
+
echo ""
|
|
40
|
+
echo "Examples:"
|
|
41
|
+
echo " $0 'Y2lzY29zcGFyazovL...' 'Hello world!'"
|
|
42
|
+
echo " $0 --space-title Engineering 'Build completed'"
|
|
43
|
+
exit 1
|
|
44
|
+
else
|
|
45
|
+
SPACE_ID="$1"
|
|
46
|
+
MESSAGE="$2"
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# Function to send message with retry logic
|
|
50
|
+
send_message() {
|
|
51
|
+
local space_id=$1
|
|
52
|
+
local message=$2
|
|
53
|
+
local max_attempts=3
|
|
54
|
+
local attempt=1
|
|
55
|
+
|
|
56
|
+
while [ $attempt -le $max_attempts ]; do
|
|
57
|
+
echo -e "${YELLOW}Attempt $attempt/$max_attempts...${NC}"
|
|
58
|
+
|
|
59
|
+
# Send message and capture result
|
|
60
|
+
RESULT=$(agent-webex message send "$space_id" "$message" 2>&1)
|
|
61
|
+
|
|
62
|
+
# Check if successful (has an 'id' field)
|
|
63
|
+
if echo "$RESULT" | jq -e '.id' > /dev/null 2>&1; then
|
|
64
|
+
echo -e "${GREEN}Message sent successfully!${NC}"
|
|
65
|
+
|
|
66
|
+
MSG_ID=$(echo "$RESULT" | jq -r '.id')
|
|
67
|
+
|
|
68
|
+
echo ""
|
|
69
|
+
echo "Message details:"
|
|
70
|
+
echo " Space: $space_id"
|
|
71
|
+
echo " Message ID: $MSG_ID"
|
|
72
|
+
|
|
73
|
+
return 0
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
# Extract error information
|
|
77
|
+
if echo "$RESULT" | jq -e '.error' > /dev/null 2>&1; then
|
|
78
|
+
ERROR_MSG=$(echo "$RESULT" | jq -r '.error // "Unknown error"')
|
|
79
|
+
echo -e "${RED}Failed: $ERROR_MSG${NC}"
|
|
80
|
+
|
|
81
|
+
# Don't retry on auth errors
|
|
82
|
+
if echo "$ERROR_MSG" | grep -Eqi "401|unauthorized|not authenticated"; then
|
|
83
|
+
echo ""
|
|
84
|
+
echo "Not authenticated. Run:"
|
|
85
|
+
echo " agent-webex auth login --token <token>"
|
|
86
|
+
return 1
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
# Don't retry on not-found errors
|
|
90
|
+
if echo "$ERROR_MSG" | grep -qi "not found"; then
|
|
91
|
+
echo ""
|
|
92
|
+
echo "Space '$space_id' not found. Check space ID."
|
|
93
|
+
echo "List spaces with: agent-webex space list"
|
|
94
|
+
return 1
|
|
95
|
+
fi
|
|
96
|
+
else
|
|
97
|
+
echo -e "${RED}Unexpected error: $RESULT${NC}"
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
# Exponential backoff before retry
|
|
101
|
+
if [ $attempt -lt $max_attempts ]; then
|
|
102
|
+
SLEEP_TIME=$((attempt * 2))
|
|
103
|
+
echo "Retrying in ${SLEEP_TIME}s..."
|
|
104
|
+
sleep $SLEEP_TIME
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
attempt=$((attempt + 1))
|
|
108
|
+
done
|
|
109
|
+
|
|
110
|
+
echo -e "${RED}Failed after $max_attempts attempts${NC}"
|
|
111
|
+
return 1
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
# Check if agent-webex is installed
|
|
115
|
+
if ! command -v agent-webex &> /dev/null; then
|
|
116
|
+
echo -e "${RED}Error: agent-webex not found${NC}"
|
|
117
|
+
echo ""
|
|
118
|
+
echo "Install it with:"
|
|
119
|
+
echo " npm install -g agent-messenger"
|
|
120
|
+
exit 1
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
# Check authentication
|
|
124
|
+
echo "Checking authentication..."
|
|
125
|
+
AUTH_STATUS=$(agent-webex auth status 2>&1)
|
|
126
|
+
|
|
127
|
+
if echo "$AUTH_STATUS" | jq -e '.error' > /dev/null 2>&1; then
|
|
128
|
+
echo -e "${RED}Not authenticated!${NC}"
|
|
129
|
+
echo ""
|
|
130
|
+
echo "Run this to authenticate:"
|
|
131
|
+
echo " agent-webex auth login --token <token>"
|
|
132
|
+
echo ""
|
|
133
|
+
echo "Get a token at: https://developer.webex.com/docs/getting-started"
|
|
134
|
+
exit 1
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
USER_NAME=$(echo "$AUTH_STATUS" | jq -r '.displayName // "Unknown"')
|
|
138
|
+
echo -e "${GREEN}Authenticated as: $USER_NAME${NC}"
|
|
139
|
+
|
|
140
|
+
# If space title provided, look up space ID
|
|
141
|
+
if [ -n "$SPACE_TITLE" ]; then
|
|
142
|
+
echo "Looking up space '$SPACE_TITLE'..."
|
|
143
|
+
SPACES=$(agent-webex space list 2>&1)
|
|
144
|
+
|
|
145
|
+
if echo "$SPACES" | jq -e '.error' > /dev/null 2>&1; then
|
|
146
|
+
echo -e "${RED}Failed to list spaces${NC}"
|
|
147
|
+
exit 1
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
SPACE_ID=$(echo "$SPACES" | jq -r --arg title "$SPACE_TITLE" '.[] | select(.title==$title) | .id')
|
|
151
|
+
|
|
152
|
+
if [ -z "$SPACE_ID" ]; then
|
|
153
|
+
echo -e "${RED}Space '$SPACE_TITLE' not found${NC}"
|
|
154
|
+
echo ""
|
|
155
|
+
echo "Available spaces:"
|
|
156
|
+
echo "$SPACES" | jq -r '.[] | " \(.title) (\(.id))"'
|
|
157
|
+
exit 1
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
echo -e "${GREEN}Found space: $SPACE_ID${NC}"
|
|
161
|
+
fi
|
|
162
|
+
|
|
163
|
+
echo ""
|
|
164
|
+
|
|
165
|
+
# Send the message
|
|
166
|
+
echo "Sending message to space $SPACE_ID..."
|
|
167
|
+
echo "Message: $MESSAGE"
|
|
168
|
+
echo ""
|
|
169
|
+
|
|
170
|
+
send_message "$SPACE_ID" "$MESSAGE"
|
package/src/cli.ts
CHANGED
|
@@ -68,6 +68,10 @@ program.command('channeltalkbot', 'Interact with Channel Talk using API credenti
|
|
|
68
68
|
executableFile: join(__dirname, 'platforms', 'channeltalkbot', `cli${ext}`),
|
|
69
69
|
})
|
|
70
70
|
|
|
71
|
+
program.command('webex', 'Interact with Cisco Webex', {
|
|
72
|
+
executableFile: join(__dirname, 'platforms', 'webex', `cli${ext}`),
|
|
73
|
+
})
|
|
74
|
+
|
|
71
75
|
program.command('tui', 'Launch unified messenger TUI', {
|
|
72
76
|
executableFile: join(__dirname, 'tui', `cli${ext}`),
|
|
73
77
|
})
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from 'bun:test'
|
|
2
|
+
import { getWebexAppCredentials } from './app-config'
|
|
3
|
+
|
|
4
|
+
const ENV_KEYS = [
|
|
5
|
+
'AGENT_WEBEX_CLIENT_ID',
|
|
6
|
+
'AGENT_WEBEX_CLIENT_SECRET',
|
|
7
|
+
'AGENT_MESSENGER_WEBEX_CLIENT_ID',
|
|
8
|
+
'AGENT_MESSENGER_WEBEX_CLIENT_SECRET',
|
|
9
|
+
] as const
|
|
10
|
+
|
|
11
|
+
describe('webex app config', () => {
|
|
12
|
+
let savedEnv: Record<string, string | undefined> = {}
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
savedEnv = {}
|
|
16
|
+
for (const key of ENV_KEYS) {
|
|
17
|
+
savedEnv[key] = process.env[key]
|
|
18
|
+
delete process.env[key]
|
|
19
|
+
}
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
for (const key of ENV_KEYS) {
|
|
24
|
+
if (savedEnv[key] === undefined) {
|
|
25
|
+
delete process.env[key]
|
|
26
|
+
} else {
|
|
27
|
+
process.env[key] = savedEnv[key]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('returns env credentials when primary env vars are set', () => {
|
|
33
|
+
process.env.AGENT_WEBEX_CLIENT_ID = 'my-client-id'
|
|
34
|
+
process.env.AGENT_WEBEX_CLIENT_SECRET = 'my-client-secret'
|
|
35
|
+
|
|
36
|
+
expect(getWebexAppCredentials()).toEqual({
|
|
37
|
+
clientId: 'my-client-id',
|
|
38
|
+
clientSecret: 'my-client-secret',
|
|
39
|
+
source: 'env',
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test('returns env credentials when legacy env vars are set', () => {
|
|
44
|
+
process.env.AGENT_MESSENGER_WEBEX_CLIENT_ID = 'legacy-client-id'
|
|
45
|
+
process.env.AGENT_MESSENGER_WEBEX_CLIENT_SECRET = 'legacy-client-secret'
|
|
46
|
+
|
|
47
|
+
expect(getWebexAppCredentials()).toEqual({
|
|
48
|
+
clientId: 'legacy-client-id',
|
|
49
|
+
clientSecret: 'legacy-client-secret',
|
|
50
|
+
source: 'env',
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test('returns builtin credentials when nothing is configured', () => {
|
|
55
|
+
const result = getWebexAppCredentials()
|
|
56
|
+
expect(result.source).toBe('builtin')
|
|
57
|
+
expect(result.clientId).toBeTruthy()
|
|
58
|
+
expect(result.clientSecret).toBeTruthy()
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test('returns builtin when only clientId is set', () => {
|
|
62
|
+
process.env.AGENT_WEBEX_CLIENT_ID = 'my-client-id'
|
|
63
|
+
|
|
64
|
+
const result = getWebexAppCredentials()
|
|
65
|
+
expect(result.source).toBe('builtin')
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test('returns builtin when only clientSecret is set', () => {
|
|
69
|
+
process.env.AGENT_WEBEX_CLIENT_SECRET = 'my-client-secret'
|
|
70
|
+
|
|
71
|
+
const result = getWebexAppCredentials()
|
|
72
|
+
expect(result.source).toBe('builtin')
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
test('trims whitespace from clientId and clientSecret', () => {
|
|
76
|
+
process.env.AGENT_WEBEX_CLIENT_ID = ' my-client-id '
|
|
77
|
+
process.env.AGENT_WEBEX_CLIENT_SECRET = ' my-client-secret '
|
|
78
|
+
|
|
79
|
+
expect(getWebexAppCredentials()).toEqual({
|
|
80
|
+
clientId: 'my-client-id',
|
|
81
|
+
clientSecret: 'my-client-secret',
|
|
82
|
+
source: 'env',
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test('primary env takes precedence over legacy env', () => {
|
|
87
|
+
process.env.AGENT_WEBEX_CLIENT_ID = 'primary-id'
|
|
88
|
+
process.env.AGENT_WEBEX_CLIENT_SECRET = 'primary-secret'
|
|
89
|
+
process.env.AGENT_MESSENGER_WEBEX_CLIENT_ID = 'legacy-id'
|
|
90
|
+
process.env.AGENT_MESSENGER_WEBEX_CLIENT_SECRET = 'legacy-secret'
|
|
91
|
+
|
|
92
|
+
expect(getWebexAppCredentials()).toEqual({
|
|
93
|
+
clientId: 'primary-id',
|
|
94
|
+
clientSecret: 'primary-secret',
|
|
95
|
+
source: 'env',
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
})
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const BUILTIN_CLIENT_ID = 'C720341c19d8dfb4cab8a5db78be4bc6d5c3983fbe84df94be34c8aa69a695583'
|
|
2
|
+
const BUILTIN_CLIENT_SECRET = 'e90806657443a7f16093c0846690aeeea96cd2b3ed9b79cf544297c526b4f9af'
|
|
3
|
+
|
|
4
|
+
export interface WebexAppCredentials {
|
|
5
|
+
clientId: string
|
|
6
|
+
clientSecret: string
|
|
7
|
+
source: 'env' | 'builtin'
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function parseTrimmed(value: string | undefined): string | undefined {
|
|
11
|
+
const normalized = value?.trim()
|
|
12
|
+
return normalized ? normalized : undefined
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getWebexAppCredentials(): WebexAppCredentials {
|
|
16
|
+
const envClientId = parseTrimmed(process.env.AGENT_WEBEX_CLIENT_ID)
|
|
17
|
+
const envClientSecret = parseTrimmed(process.env.AGENT_WEBEX_CLIENT_SECRET)
|
|
18
|
+
|
|
19
|
+
if (envClientId && envClientSecret) {
|
|
20
|
+
return { clientId: envClientId, clientSecret: envClientSecret, source: 'env' }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const legacyClientId = parseTrimmed(process.env.AGENT_MESSENGER_WEBEX_CLIENT_ID)
|
|
24
|
+
const legacyClientSecret = parseTrimmed(process.env.AGENT_MESSENGER_WEBEX_CLIENT_SECRET)
|
|
25
|
+
|
|
26
|
+
if (legacyClientId && legacyClientSecret) {
|
|
27
|
+
return { clientId: legacyClientId, clientSecret: legacyClientSecret, source: 'env' }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return { clientId: BUILTIN_CLIENT_ID, clientSecret: BUILTIN_CLIENT_SECRET, source: 'builtin' }
|
|
31
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
import { spawn } from 'bun'
|
|
4
|
+
|
|
5
|
+
import pkg from '../../../package.json' with { type: 'json' }
|
|
6
|
+
|
|
7
|
+
describe('Webex CLI program structure', () => {
|
|
8
|
+
test('--help shows all commands', async () => {
|
|
9
|
+
const proc = spawn(['bun', 'run', './src/platforms/webex/cli.ts', '--help'], {
|
|
10
|
+
cwd: process.cwd(),
|
|
11
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const output = await new Response(proc.stdout).text()
|
|
15
|
+
|
|
16
|
+
expect(output).toContain('auth')
|
|
17
|
+
expect(output).toContain('member')
|
|
18
|
+
expect(output).toContain('message')
|
|
19
|
+
expect(output).toContain('snapshot')
|
|
20
|
+
expect(output).toContain('space')
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test('--version shows package version', async () => {
|
|
24
|
+
const proc = spawn(['bun', 'run', './src/platforms/webex/cli.ts', '--version'], {
|
|
25
|
+
cwd: process.cwd(),
|
|
26
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const output = await new Response(proc.stdout).text()
|
|
30
|
+
expect(output.trim()).toBe(pkg.version)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test('auth login --help shows Device Grant options', async () => {
|
|
34
|
+
const proc = spawn(['bun', 'run', './src/platforms/webex/cli.ts', 'auth', 'login', '--help'], {
|
|
35
|
+
cwd: process.cwd(),
|
|
36
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const output = await new Response(proc.stdout).text()
|
|
40
|
+
|
|
41
|
+
expect(output).toContain('--token')
|
|
42
|
+
expect(output).toContain('--client-id')
|
|
43
|
+
expect(output).toContain('--client-secret')
|
|
44
|
+
expect(output).toContain('--pretty')
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test('message dm --help shows email argument', async () => {
|
|
48
|
+
const proc = spawn(['bun', 'run', './src/platforms/webex/cli.ts', 'message', 'dm', '--help'], {
|
|
49
|
+
cwd: process.cwd(),
|
|
50
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
const output = await new Response(proc.stdout).text()
|
|
54
|
+
|
|
55
|
+
expect(output).toContain('email')
|
|
56
|
+
expect(output).toContain('--markdown')
|
|
57
|
+
})
|
|
58
|
+
})
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import type { Command as CommandType } from 'commander'
|
|
4
|
+
import { Command } from 'commander'
|
|
5
|
+
|
|
6
|
+
import pkg from '../../../package.json' with { type: 'json' }
|
|
7
|
+
import { authCommand, memberCommand, messageCommand, snapshotCommand, spaceCommand } from './commands'
|
|
8
|
+
import { ensureWebexAuth } from './ensure-auth'
|
|
9
|
+
|
|
10
|
+
function isAuthCommand(command: CommandType): boolean {
|
|
11
|
+
let cmd: CommandType | null = command
|
|
12
|
+
while (cmd) {
|
|
13
|
+
if (cmd.name() === 'auth') return true
|
|
14
|
+
cmd = cmd.parent
|
|
15
|
+
}
|
|
16
|
+
return false
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const program = new Command()
|
|
20
|
+
|
|
21
|
+
program
|
|
22
|
+
.name('agent-webex')
|
|
23
|
+
.description('CLI tool for Cisco Webex communication')
|
|
24
|
+
.version(pkg.version)
|
|
25
|
+
|
|
26
|
+
program.hook('preAction', async (_thisCommand, actionCommand) => {
|
|
27
|
+
if (isAuthCommand(actionCommand)) return
|
|
28
|
+
await ensureWebexAuth()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
program.addCommand(authCommand)
|
|
32
|
+
program.addCommand(memberCommand)
|
|
33
|
+
program.addCommand(messageCommand)
|
|
34
|
+
program.addCommand(snapshotCommand)
|
|
35
|
+
program.addCommand(spaceCommand)
|
|
36
|
+
|
|
37
|
+
program.parse(process.argv)
|
|
38
|
+
|
|
39
|
+
export default program
|