signalwire-agents 1.0.15__py3-none-any.whl → 1.0.16.dev1__py3-none-any.whl
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.
- signalwire_agents/__init__.py +1 -1
- signalwire_agents/agent_server.py +1 -1
- signalwire_agents/cli/dokku.py +118 -221
- signalwire_agents/core/agent_base.py +4 -3
- signalwire_agents/core/swml_service.py +4 -3
- {signalwire_agents-1.0.15.dist-info → signalwire_agents-1.0.16.dev1.dist-info}/METADATA +1 -1
- {signalwire_agents-1.0.15.dist-info → signalwire_agents-1.0.16.dev1.dist-info}/RECORD +14 -14
- {signalwire_agents-1.0.15.data → signalwire_agents-1.0.16.dev1.data}/data/share/man/man1/sw-agent-init.1 +0 -0
- {signalwire_agents-1.0.15.data → signalwire_agents-1.0.16.dev1.data}/data/share/man/man1/sw-search.1 +0 -0
- {signalwire_agents-1.0.15.data → signalwire_agents-1.0.16.dev1.data}/data/share/man/man1/swaig-test.1 +0 -0
- {signalwire_agents-1.0.15.dist-info → signalwire_agents-1.0.16.dev1.dist-info}/WHEEL +0 -0
- {signalwire_agents-1.0.15.dist-info → signalwire_agents-1.0.16.dev1.dist-info}/entry_points.txt +0 -0
- {signalwire_agents-1.0.15.dist-info → signalwire_agents-1.0.16.dev1.dist-info}/licenses/LICENSE +0 -0
- {signalwire_agents-1.0.15.dist-info → signalwire_agents-1.0.16.dev1.dist-info}/top_level.txt +0 -0
signalwire_agents/__init__.py
CHANGED
|
@@ -18,7 +18,7 @@ A package for building AI agents using SignalWire's AI and SWML capabilities.
|
|
|
18
18
|
from .core.logging_config import configure_logging
|
|
19
19
|
configure_logging()
|
|
20
20
|
|
|
21
|
-
__version__ = "1.0.
|
|
21
|
+
__version__ = "1.0.16.dev1"
|
|
22
22
|
|
|
23
23
|
# Import core classes for easier access
|
|
24
24
|
from .core.agent_base import AgentBase
|
signalwire_agents/cli/dokku.py
CHANGED
|
@@ -92,7 +92,7 @@ PROCFILE_TEMPLATE = """web: gunicorn app:app --bind 0.0.0.0:$PORT --workers 2 --
|
|
|
92
92
|
RUNTIME_TEMPLATE = """python-3.11
|
|
93
93
|
"""
|
|
94
94
|
|
|
95
|
-
REQUIREMENTS_TEMPLATE = """signalwire-agents>=1.0.
|
|
95
|
+
REQUIREMENTS_TEMPLATE = """signalwire-agents>=1.0.16
|
|
96
96
|
gunicorn>=21.0.0
|
|
97
97
|
uvicorn>=0.24.0
|
|
98
98
|
python-dotenv>=1.0.0
|
|
@@ -150,8 +150,8 @@ SIGNALWIRE_TOKEN=your-api-token
|
|
|
150
150
|
# This should be your publicly accessible URL (e.g., ngrok, dokku domain)
|
|
151
151
|
SWML_PROXY_URL_BASE=https://your-app.example.com
|
|
152
152
|
|
|
153
|
-
# Basic Auth for SWML endpoints (
|
|
154
|
-
SWML_BASIC_AUTH_USER=
|
|
153
|
+
# Basic Auth for SWML endpoints (password required, user defaults to 'signalwire')
|
|
154
|
+
# SWML_BASIC_AUTH_USER=signalwire
|
|
155
155
|
SWML_BASIC_AUTH_PASSWORD=your-secure-password
|
|
156
156
|
|
|
157
157
|
# Agent Configuration
|
|
@@ -383,8 +383,8 @@ def setup_swml_handler():
|
|
|
383
383
|
project = os.getenv("SIGNALWIRE_PROJECT_ID", "")
|
|
384
384
|
token = os.getenv("SIGNALWIRE_TOKEN", "")
|
|
385
385
|
agent_name = os.getenv("AGENT_NAME", "{agent_slug}")
|
|
386
|
-
proxy_url = os.getenv("SWML_PROXY_URL_BASE", "")
|
|
387
|
-
auth_user = os.getenv("SWML_BASIC_AUTH_USER", "")
|
|
386
|
+
proxy_url = os.getenv("SWML_PROXY_URL_BASE", os.getenv("APP_URL", ""))
|
|
387
|
+
auth_user = os.getenv("SWML_BASIC_AUTH_USER", "signalwire")
|
|
388
388
|
auth_pass = os.getenv("SWML_BASIC_AUTH_PASSWORD", "")
|
|
389
389
|
|
|
390
390
|
if not all([sw_host, project, token]):
|
|
@@ -395,8 +395,8 @@ def setup_swml_handler():
|
|
|
395
395
|
print("SWML_PROXY_URL_BASE not set - skipping SWML handler setup")
|
|
396
396
|
return
|
|
397
397
|
|
|
398
|
-
# Build SWML URL with basic auth
|
|
399
|
-
if
|
|
398
|
+
# Build SWML URL with basic auth (user defaults to 'signalwire' if not set)
|
|
399
|
+
if auth_pass and "://" in proxy_url:
|
|
400
400
|
scheme, rest = proxy_url.split("://", 1)
|
|
401
401
|
swml_url = f"{{scheme}}://{{auth_user}}:{{auth_pass}}@{{rest}}/swml"
|
|
402
402
|
else:
|
|
@@ -411,19 +411,17 @@ def setup_swml_handler():
|
|
|
411
411
|
swml_handler_info["address_id"] = existing["address_id"]
|
|
412
412
|
swml_handler_info["address"] = existing["address"]
|
|
413
413
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
else:
|
|
426
|
-
print(f"Using existing SWML handler: {{existing['name']}}")
|
|
414
|
+
# Always update the URL on startup to ensure credentials are current
|
|
415
|
+
try:
|
|
416
|
+
requests.put(
|
|
417
|
+
f"https://{{sw_host}}/api/fabric/resources/external_swml_handlers/{{existing['id']}}",
|
|
418
|
+
json={{"primary_request_url": swml_url, "primary_request_method": "POST"}},
|
|
419
|
+
auth=auth,
|
|
420
|
+
headers=headers
|
|
421
|
+
)
|
|
422
|
+
print(f"Updated SWML handler: {{existing['name']}}")
|
|
423
|
+
except Exception as e:
|
|
424
|
+
print(f"Failed to update handler URL: {{e}}")
|
|
427
425
|
print(f"Call address: {{existing['address']}}")
|
|
428
426
|
else:
|
|
429
427
|
try:
|
|
@@ -1111,9 +1109,11 @@ curl -X POST <span class="base-url"></span>/swml/swaig/ \\
|
|
|
1111
1109
|
return div.innerHTML;
|
|
1112
1110
|
}}
|
|
1113
1111
|
|
|
1114
|
-
// WebRTC calling
|
|
1112
|
+
// WebRTC calling - robust pattern from santa app.js
|
|
1115
1113
|
let client = null;
|
|
1116
1114
|
let roomSession = null;
|
|
1115
|
+
let currentToken = null;
|
|
1116
|
+
let currentDestination = null;
|
|
1117
1117
|
|
|
1118
1118
|
const connectBtn = document.getElementById('connectBtn');
|
|
1119
1119
|
const disconnectBtn = document.getElementById('disconnectBtn');
|
|
@@ -1125,8 +1125,15 @@ curl -X POST <span class="base-url"></span>/swml/swaig/ \\
|
|
|
1125
1125
|
}}
|
|
1126
1126
|
|
|
1127
1127
|
async function connect() {{
|
|
1128
|
+
// Debounce - prevent double-clicks
|
|
1129
|
+
if (connectBtn.disabled) {{
|
|
1130
|
+
console.log('Call already in progress');
|
|
1131
|
+
return;
|
|
1132
|
+
}}
|
|
1133
|
+
connectBtn.disabled = true;
|
|
1134
|
+
connectBtn.textContent = 'Connecting...';
|
|
1135
|
+
|
|
1128
1136
|
try {{
|
|
1129
|
-
connectBtn.disabled = true;
|
|
1130
1137
|
updateCallStatus('Getting token...');
|
|
1131
1138
|
|
|
1132
1139
|
const tokenResp = await fetch('/get_token');
|
|
@@ -1136,17 +1143,28 @@ curl -X POST <span class="base-url"></span>/swml/swaig/ \\
|
|
|
1136
1143
|
throw new Error(tokenData.error);
|
|
1137
1144
|
}}
|
|
1138
1145
|
|
|
1146
|
+
currentToken = tokenData.token;
|
|
1147
|
+
currentDestination = tokenData.address;
|
|
1148
|
+
|
|
1139
1149
|
if (tokenData.address) {{
|
|
1140
1150
|
destinationInput.value = tokenData.address;
|
|
1141
1151
|
}}
|
|
1142
1152
|
|
|
1153
|
+
console.log('Got token, destination:', currentDestination);
|
|
1143
1154
|
updateCallStatus('Connecting...');
|
|
1144
1155
|
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1156
|
+
// Initialize SignalWire client
|
|
1157
|
+
if (window.SignalWire && typeof window.SignalWire.SignalWire === 'function') {{
|
|
1158
|
+
console.log('Initializing SignalWire client...');
|
|
1159
|
+
client = await window.SignalWire.SignalWire({{
|
|
1160
|
+
token: currentToken
|
|
1161
|
+
}});
|
|
1162
|
+
}} else {{
|
|
1163
|
+
console.error('SignalWire SDK structure:', window.SignalWire);
|
|
1164
|
+
throw new Error('SignalWire.SignalWire function not found');
|
|
1165
|
+
}}
|
|
1148
1166
|
|
|
1149
|
-
const destination =
|
|
1167
|
+
const destination = currentDestination || destinationInput.value;
|
|
1150
1168
|
roomSession = await client.dial({{
|
|
1151
1169
|
to: destination,
|
|
1152
1170
|
audio: {{
|
|
@@ -1157,22 +1175,37 @@ curl -X POST <span class="base-url"></span>/swml/swaig/ \\
|
|
|
1157
1175
|
video: false
|
|
1158
1176
|
}});
|
|
1159
1177
|
|
|
1178
|
+
console.log('Room session created:', roomSession);
|
|
1179
|
+
|
|
1160
1180
|
roomSession.on('call.joined', () => {{
|
|
1181
|
+
console.log('Call joined');
|
|
1161
1182
|
updateCallStatus('Connected');
|
|
1162
1183
|
disconnectBtn.disabled = false;
|
|
1163
1184
|
}});
|
|
1164
1185
|
|
|
1165
1186
|
roomSession.on('call.left', () => {{
|
|
1187
|
+
console.log('Call left');
|
|
1166
1188
|
updateCallStatus('Call ended');
|
|
1167
1189
|
cleanup();
|
|
1168
1190
|
}});
|
|
1169
1191
|
|
|
1170
1192
|
roomSession.on('destroy', () => {{
|
|
1193
|
+
console.log('Session destroyed');
|
|
1171
1194
|
updateCallStatus('Call ended');
|
|
1172
1195
|
cleanup();
|
|
1173
1196
|
}});
|
|
1174
1197
|
|
|
1198
|
+
roomSession.on('room.left', () => {{
|
|
1199
|
+
console.log('Room left');
|
|
1200
|
+
cleanup();
|
|
1201
|
+
}});
|
|
1202
|
+
|
|
1175
1203
|
await roomSession.start();
|
|
1204
|
+
console.log('Call started successfully');
|
|
1205
|
+
|
|
1206
|
+
// Update UI
|
|
1207
|
+
connectBtn.style.display = 'none';
|
|
1208
|
+
disconnectBtn.style.display = 'inline-block';
|
|
1176
1209
|
|
|
1177
1210
|
}} catch (err) {{
|
|
1178
1211
|
console.error('Connection error:', err);
|
|
@@ -1182,26 +1215,65 @@ curl -X POST <span class="base-url"></span>/swml/swaig/ \\
|
|
|
1182
1215
|
}}
|
|
1183
1216
|
|
|
1184
1217
|
async function disconnect() {{
|
|
1218
|
+
console.log('Disconnect called');
|
|
1219
|
+
await hangup();
|
|
1220
|
+
}}
|
|
1221
|
+
|
|
1222
|
+
async function hangup() {{
|
|
1185
1223
|
try {{
|
|
1186
1224
|
if (roomSession) {{
|
|
1225
|
+
console.log('Hanging up call...');
|
|
1187
1226
|
await roomSession.hangup();
|
|
1227
|
+
console.log('Call hung up successfully');
|
|
1188
1228
|
}}
|
|
1189
1229
|
}} catch (err) {{
|
|
1190
|
-
console.error('
|
|
1230
|
+
console.error('Hangup error:', err);
|
|
1191
1231
|
}}
|
|
1192
1232
|
cleanup();
|
|
1193
1233
|
}}
|
|
1194
1234
|
|
|
1195
1235
|
function cleanup() {{
|
|
1236
|
+
console.log('Cleanup called');
|
|
1237
|
+
|
|
1238
|
+
// Clean up local stream if it exists
|
|
1239
|
+
if (roomSession && roomSession.localStream) {{
|
|
1240
|
+
console.log('Stopping local stream tracks');
|
|
1241
|
+
roomSession.localStream.getTracks().forEach(track => {{
|
|
1242
|
+
track.stop();
|
|
1243
|
+
}});
|
|
1244
|
+
}}
|
|
1245
|
+
|
|
1246
|
+
roomSession = null;
|
|
1247
|
+
|
|
1248
|
+
// Disconnect the client properly
|
|
1249
|
+
if (client) {{
|
|
1250
|
+
try {{
|
|
1251
|
+
console.log('Disconnecting client');
|
|
1252
|
+
client.disconnect();
|
|
1253
|
+
}} catch (e) {{
|
|
1254
|
+
console.log('Client disconnect error:', e);
|
|
1255
|
+
}}
|
|
1256
|
+
client = null;
|
|
1257
|
+
}}
|
|
1258
|
+
|
|
1259
|
+
// Reset UI
|
|
1196
1260
|
connectBtn.disabled = false;
|
|
1261
|
+
connectBtn.textContent = 'Call Agent';
|
|
1262
|
+
connectBtn.style.display = 'inline-block';
|
|
1197
1263
|
disconnectBtn.disabled = true;
|
|
1198
|
-
|
|
1199
|
-
client = null;
|
|
1264
|
+
disconnectBtn.style.display = 'none';
|
|
1200
1265
|
}}
|
|
1201
1266
|
|
|
1202
1267
|
connectBtn.addEventListener('click', connect);
|
|
1203
1268
|
disconnectBtn.addEventListener('click', disconnect);
|
|
1204
1269
|
|
|
1270
|
+
// Clean up on page unload
|
|
1271
|
+
window.addEventListener('beforeunload', () => {{
|
|
1272
|
+
if (roomSession) {{
|
|
1273
|
+
hangup();
|
|
1274
|
+
}}
|
|
1275
|
+
}});
|
|
1276
|
+
|
|
1205
1277
|
// Initialize on load
|
|
1206
1278
|
document.addEventListener('DOMContentLoaded', async function() {{
|
|
1207
1279
|
const baseUrl = window.location.origin;
|
|
@@ -1448,7 +1520,7 @@ swaig-test app.py --list-tools
|
|
|
1448
1520
|
# Templates - CI/CD Mode
|
|
1449
1521
|
# =============================================================================
|
|
1450
1522
|
|
|
1451
|
-
DEPLOY_WORKFLOW_TEMPLATE = '''#
|
|
1523
|
+
DEPLOY_WORKFLOW_TEMPLATE = '''# Deploy to Dokku - calls reusable workflow from dokku-deploy-system
|
|
1452
1524
|
name: Deploy
|
|
1453
1525
|
|
|
1454
1526
|
on:
|
|
@@ -1456,138 +1528,24 @@ on:
|
|
|
1456
1528
|
push:
|
|
1457
1529
|
branches: [main, staging, develop]
|
|
1458
1530
|
|
|
1531
|
+
permissions:
|
|
1532
|
+
contents: read
|
|
1533
|
+
deployments: write
|
|
1534
|
+
|
|
1459
1535
|
concurrency:
|
|
1460
1536
|
group: deploy-${{{{ github.ref }}}}
|
|
1461
1537
|
cancel-in-progress: true
|
|
1462
1538
|
|
|
1463
1539
|
jobs:
|
|
1464
1540
|
deploy:
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
- uses: actions/checkout@v4
|
|
1471
|
-
with:
|
|
1472
|
-
fetch-depth: 0
|
|
1473
|
-
|
|
1474
|
-
- name: Set variables
|
|
1475
|
-
id: vars
|
|
1476
|
-
run: |
|
|
1477
|
-
BRANCH="${{GITHUB_REF#refs/heads/}}"
|
|
1478
|
-
case "$BRANCH" in
|
|
1479
|
-
main) APP="${{BASE_APP_NAME}}"; ENV="production" ;;
|
|
1480
|
-
staging) APP="${{BASE_APP_NAME}}-staging"; ENV="staging" ;;
|
|
1481
|
-
develop) APP="${{BASE_APP_NAME}}-dev"; ENV="development" ;;
|
|
1482
|
-
*) APP="${{BASE_APP_NAME}}"; ENV="production" ;;
|
|
1483
|
-
esac
|
|
1484
|
-
echo "app_name=$APP" >> $GITHUB_OUTPUT
|
|
1485
|
-
echo "environment=$ENV" >> $GITHUB_OUTPUT
|
|
1486
|
-
|
|
1487
|
-
- name: Setup SSH
|
|
1488
|
-
run: |
|
|
1489
|
-
mkdir -p ~/.ssh && chmod 700 ~/.ssh
|
|
1490
|
-
echo "${{{{ secrets.DOKKU_SSH_PRIVATE_KEY }}}}" > ~/.ssh/key && chmod 600 ~/.ssh/key
|
|
1491
|
-
ssh-keyscan -H ${{{{ secrets.DOKKU_HOST }}}} >> ~/.ssh/known_hosts
|
|
1492
|
-
echo -e "Host dokku\\n HostName ${{{{ secrets.DOKKU_HOST }}}}\\n User dokku\\n IdentityFile ~/.ssh/key" > ~/.ssh/config
|
|
1493
|
-
|
|
1494
|
-
- name: Create app
|
|
1495
|
-
run: |
|
|
1496
|
-
APP_NAME="${{{{ steps.vars.outputs.app_name }}}}"
|
|
1497
|
-
ssh dokku apps:exists $APP_NAME 2>/dev/null || ssh dokku apps:create $APP_NAME
|
|
1498
|
-
|
|
1499
|
-
- name: Unlock app
|
|
1500
|
-
run: |
|
|
1501
|
-
APP_NAME="${{{{ steps.vars.outputs.app_name }}}}"
|
|
1502
|
-
ssh dokku apps:unlock $APP_NAME 2>/dev/null || true
|
|
1503
|
-
|
|
1504
|
-
- name: Configure env
|
|
1505
|
-
run: |
|
|
1506
|
-
APP_NAME="${{{{ steps.vars.outputs.app_name }}}}"
|
|
1507
|
-
DOMAIN="${{APP_NAME}}.${{{{ secrets.BASE_DOMAIN }}}}"
|
|
1508
|
-
ssh dokku config:set --no-restart $APP_NAME \\
|
|
1509
|
-
APP_ENV="${{{{ steps.vars.outputs.environment }}}}" \\
|
|
1510
|
-
APP_URL="https://${{DOMAIN}}" \\
|
|
1511
|
-
SWML_BASIC_AUTH_USER="${{{{ secrets.SWML_BASIC_AUTH_USER }}}}" \\
|
|
1512
|
-
SWML_BASIC_AUTH_PASSWORD="${{{{ secrets.SWML_BASIC_AUTH_PASSWORD }}}}"
|
|
1513
|
-
|
|
1514
|
-
- name: Deploy
|
|
1515
|
-
run: |
|
|
1516
|
-
APP_NAME="${{{{ steps.vars.outputs.app_name }}}}"
|
|
1517
|
-
git remote add dokku dokku@${{{{ secrets.DOKKU_HOST }}}}:$APP_NAME 2>/dev/null || true
|
|
1518
|
-
GIT_SSH_COMMAND="ssh -i ~/.ssh/key" git push dokku HEAD:main -f
|
|
1519
|
-
|
|
1520
|
-
- name: Configure domain
|
|
1521
|
-
run: |
|
|
1522
|
-
APP_NAME="${{{{ steps.vars.outputs.app_name }}}}"
|
|
1523
|
-
DOMAIN="${{APP_NAME}}.${{{{ secrets.BASE_DOMAIN }}}}"
|
|
1524
|
-
ssh dokku domains:clear $APP_NAME 2>/dev/null || true
|
|
1525
|
-
ssh dokku domains:add $APP_NAME $DOMAIN
|
|
1526
|
-
|
|
1527
|
-
- name: SSL
|
|
1528
|
-
run: |
|
|
1529
|
-
APP_NAME="${{{{ steps.vars.outputs.app_name }}}}"
|
|
1530
|
-
echo "Checking SSL status..."
|
|
1531
|
-
SSL_STATUS=$(ssh dokku letsencrypt:active $APP_NAME 2>/dev/null || echo "error")
|
|
1532
|
-
echo "SSL status: $SSL_STATUS"
|
|
1533
|
-
if [ "$SSL_STATUS" = "true" ]; then
|
|
1534
|
-
echo "SSL already active"
|
|
1535
|
-
else
|
|
1536
|
-
echo "Enabling SSL..."
|
|
1537
|
-
ssh dokku letsencrypt:enable $APP_NAME 2>&1 || echo "SSL enable failed"
|
|
1538
|
-
fi
|
|
1539
|
-
|
|
1540
|
-
- name: Verify
|
|
1541
|
-
id: verify
|
|
1542
|
-
run: |
|
|
1543
|
-
APP_NAME="${{{{ steps.vars.outputs.app_name }}}}"
|
|
1544
|
-
DOMAIN="${{APP_NAME}}.${{{{ secrets.BASE_DOMAIN }}}}"
|
|
1545
|
-
sleep 10
|
|
1546
|
-
if curl -sf "https://${{DOMAIN}}/health"; then
|
|
1547
|
-
echo "HTTPS OK: https://${{DOMAIN}}"
|
|
1548
|
-
echo "status=success" >> $GITHUB_OUTPUT
|
|
1549
|
-
echo "url=https://${{DOMAIN}}" >> $GITHUB_OUTPUT
|
|
1550
|
-
elif curl -sf "http://${{DOMAIN}}/health"; then
|
|
1551
|
-
echo "HTTP only: http://${{DOMAIN}}"
|
|
1552
|
-
echo "status=success" >> $GITHUB_OUTPUT
|
|
1553
|
-
echo "url=http://${{DOMAIN}}" >> $GITHUB_OUTPUT
|
|
1554
|
-
else
|
|
1555
|
-
echo "Check logs"
|
|
1556
|
-
echo "status=failed" >> $GITHUB_OUTPUT
|
|
1557
|
-
fi
|
|
1558
|
-
|
|
1559
|
-
- name: Notify Slack
|
|
1560
|
-
if: always()
|
|
1561
|
-
env:
|
|
1562
|
-
SLACK_WEBHOOK_URL: ${{{{ secrets.SLACK_WEBHOOK_URL }}}}
|
|
1563
|
-
run: |
|
|
1564
|
-
[ -z "$SLACK_WEBHOOK_URL" ] && exit 0
|
|
1565
|
-
APP_NAME="${{{{ steps.vars.outputs.app_name }}}}"
|
|
1566
|
-
DOMAIN="${{APP_NAME}}.${{{{ secrets.BASE_DOMAIN }}}}"
|
|
1567
|
-
if [ "${{{{ steps.verify.outputs.status }}}}" == "success" ]; then
|
|
1568
|
-
COLOR="good"
|
|
1569
|
-
STATUS="✅ Deployed"
|
|
1570
|
-
else
|
|
1571
|
-
COLOR="danger"
|
|
1572
|
-
STATUS="❌ Deploy failed"
|
|
1573
|
-
fi
|
|
1574
|
-
curl -X POST -H 'Content-type: application/json' \\
|
|
1575
|
-
--data "{{
|
|
1576
|
-
\\"attachments\\": [{{
|
|
1577
|
-
\\"color\\": \\"$COLOR\\",
|
|
1578
|
-
\\"title\\": \\"$STATUS: $APP_NAME\\",
|
|
1579
|
-
\\"fields\\": [
|
|
1580
|
-
{{\\"title\\": \\"Environment\\", \\"value\\": \\"${{{{ steps.vars.outputs.environment }}}}\\", \\"short\\": true}},
|
|
1581
|
-
{{\\"title\\": \\"Branch\\", \\"value\\": \\"${{{{ github.ref_name }}}}\\", \\"short\\": true}},
|
|
1582
|
-
{{\\"title\\": \\"URL\\", \\"value\\": \\"https://$DOMAIN\\", \\"short\\": false}}
|
|
1583
|
-
],
|
|
1584
|
-
\\"footer\\": \\"<${{{{ github.server_url }}}}/${{{{ github.repository }}}}/actions/runs/${{{{ github.run_id }}}}|View Workflow>\\"
|
|
1585
|
-
}}]
|
|
1586
|
-
}}" \\
|
|
1587
|
-
"$SLACK_WEBHOOK_URL" || true
|
|
1541
|
+
uses: signalwire-demos/dokku-deploy-system/.github/workflows/deploy.yml@main
|
|
1542
|
+
secrets: inherit
|
|
1543
|
+
# Optional: customize health check path
|
|
1544
|
+
# with:
|
|
1545
|
+
# health_check_path: '/health'
|
|
1588
1546
|
'''
|
|
1589
1547
|
|
|
1590
|
-
PREVIEW_WORKFLOW_TEMPLATE = '''# Preview environments for pull requests
|
|
1548
|
+
PREVIEW_WORKFLOW_TEMPLATE = '''# Preview environments for pull requests - calls reusable workflow from dokku-deploy-system
|
|
1591
1549
|
name: Preview
|
|
1592
1550
|
|
|
1593
1551
|
on:
|
|
@@ -1597,74 +1555,13 @@ on:
|
|
|
1597
1555
|
concurrency:
|
|
1598
1556
|
group: preview-${{{{ github.event.pull_request.number }}}}
|
|
1599
1557
|
|
|
1600
|
-
env:
|
|
1601
|
-
APP_NAME: ${{{{ github.event.repository.name }}}}-pr-${{{{ github.event.pull_request.number }}}}
|
|
1602
|
-
|
|
1603
1558
|
jobs:
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
with:
|
|
1611
|
-
fetch-depth: 0
|
|
1612
|
-
|
|
1613
|
-
- name: Setup SSH
|
|
1614
|
-
run: |
|
|
1615
|
-
mkdir -p ~/.ssh && chmod 700 ~/.ssh
|
|
1616
|
-
echo "${{{{ secrets.DOKKU_SSH_PRIVATE_KEY }}}}" > ~/.ssh/key && chmod 600 ~/.ssh/key
|
|
1617
|
-
ssh-keyscan -H ${{{{ secrets.DOKKU_HOST }}}} >> ~/.ssh/known_hosts
|
|
1618
|
-
|
|
1619
|
-
- name: Create app
|
|
1620
|
-
run: |
|
|
1621
|
-
ssh -i ~/.ssh/key dokku@${{{{ secrets.DOKKU_HOST }}}} apps:exists $APP_NAME 2>/dev/null || \\
|
|
1622
|
-
ssh -i ~/.ssh/key dokku@${{{{ secrets.DOKKU_HOST }}}} apps:create $APP_NAME
|
|
1623
|
-
|
|
1624
|
-
- name: Configure env
|
|
1625
|
-
run: |
|
|
1626
|
-
DOMAIN="${{{{ env.APP_NAME }}}}.${{{{ secrets.BASE_DOMAIN }}}}"
|
|
1627
|
-
ssh -i ~/.ssh/key dokku@${{{{ secrets.DOKKU_HOST }}}} config:set --no-restart $APP_NAME \\
|
|
1628
|
-
APP_ENV=preview \\
|
|
1629
|
-
APP_URL="https://$DOMAIN"
|
|
1630
|
-
ssh -i ~/.ssh/key dokku@${{{{ secrets.DOKKU_HOST }}}} resource:limit $APP_NAME --memory 256m || true
|
|
1631
|
-
|
|
1632
|
-
- name: Deploy
|
|
1633
|
-
run: |
|
|
1634
|
-
git remote add dokku dokku@${{{{ secrets.DOKKU_HOST }}}}:$APP_NAME 2>/dev/null || true
|
|
1635
|
-
GIT_SSH_COMMAND="ssh -i ~/.ssh/key" git push dokku HEAD:main -f
|
|
1636
|
-
|
|
1637
|
-
- name: Configure domain
|
|
1638
|
-
run: |
|
|
1639
|
-
DOMAIN="${{{{ env.APP_NAME }}}}.${{{{ secrets.BASE_DOMAIN }}}}"
|
|
1640
|
-
ssh -i ~/.ssh/key dokku@${{{{ secrets.DOKKU_HOST }}}} domains:clear $APP_NAME 2>/dev/null || true
|
|
1641
|
-
ssh -i ~/.ssh/key dokku@${{{{ secrets.DOKKU_HOST }}}} domains:add $APP_NAME $DOMAIN
|
|
1642
|
-
|
|
1643
|
-
- name: SSL
|
|
1644
|
-
run: |
|
|
1645
|
-
ssh -i ~/.ssh/key dokku@${{{{ secrets.DOKKU_HOST }}}} letsencrypt:enable $APP_NAME || true
|
|
1646
|
-
|
|
1647
|
-
- name: Comment URL
|
|
1648
|
-
uses: actions/github-script@v7
|
|
1649
|
-
with:
|
|
1650
|
-
script: |
|
|
1651
|
-
const url = `https://${{{{ env.APP_NAME }}}}.${{{{ secrets.BASE_DOMAIN }}}}`;
|
|
1652
|
-
const body = `## 🚀 Preview\\n\\n✅ Deployed: [${{url}}](${{url}})\\n\\n<sub>Auto-destroyed on PR close</sub>`;
|
|
1653
|
-
const comments = await github.rest.issues.listComments({{owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number}});
|
|
1654
|
-
const bot = comments.data.find(c => c.user.type === 'Bot' && c.body.includes('Preview'));
|
|
1655
|
-
if (bot) await github.rest.issues.updateComment({{owner: context.repo.owner, repo: context.repo.repo, comment_id: bot.id, body}});
|
|
1656
|
-
else await github.rest.issues.createComment({{owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, body}});
|
|
1657
|
-
|
|
1658
|
-
cleanup:
|
|
1659
|
-
if: github.event.action == 'closed'
|
|
1660
|
-
runs-on: ubuntu-latest
|
|
1661
|
-
steps:
|
|
1662
|
-
- name: Destroy preview
|
|
1663
|
-
run: |
|
|
1664
|
-
mkdir -p ~/.ssh
|
|
1665
|
-
echo "${{{{ secrets.DOKKU_SSH_PRIVATE_KEY }}}}" > ~/.ssh/key && chmod 600 ~/.ssh/key
|
|
1666
|
-
ssh-keyscan -H ${{{{ secrets.DOKKU_HOST }}}} >> ~/.ssh/known_hosts
|
|
1667
|
-
ssh -i ~/.ssh/key dokku@${{{{ secrets.DOKKU_HOST }}}} apps:destroy ${{{{ env.APP_NAME }}}} --force || true
|
|
1559
|
+
preview:
|
|
1560
|
+
uses: signalwire-demos/dokku-deploy-system/.github/workflows/preview.yml@main
|
|
1561
|
+
secrets: inherit
|
|
1562
|
+
# Optional: customize memory limit for previews
|
|
1563
|
+
# with:
|
|
1564
|
+
# memory_limit: '256m'
|
|
1668
1565
|
'''
|
|
1669
1566
|
|
|
1670
1567
|
DOKKU_CONFIG_TEMPLATE = '''# ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -109,7 +109,7 @@ class AgentBase(
|
|
|
109
109
|
name: str,
|
|
110
110
|
route: str = "/",
|
|
111
111
|
host: str = "0.0.0.0",
|
|
112
|
-
port: int =
|
|
112
|
+
port: Optional[int] = None,
|
|
113
113
|
basic_auth: Optional[Tuple[str, str]] = None,
|
|
114
114
|
use_pom: bool = True,
|
|
115
115
|
token_expiry_secs: int = 3600,
|
|
@@ -162,7 +162,8 @@ class AgentBase(
|
|
|
162
162
|
# Apply service config values, with constructor parameters taking precedence
|
|
163
163
|
final_route = route if route != "/" else service_config.get('route', route)
|
|
164
164
|
final_host = host if host != "0.0.0.0" else service_config.get('host', host)
|
|
165
|
-
|
|
165
|
+
# For port: use explicit param if provided, else config file, else let SWMLService use PORT env var
|
|
166
|
+
final_port = port if port is not None else service_config.get('port', None)
|
|
166
167
|
final_name = service_config.get('name', name)
|
|
167
168
|
|
|
168
169
|
# Initialize the SWMLService base class
|
|
@@ -182,7 +183,7 @@ class AgentBase(
|
|
|
182
183
|
|
|
183
184
|
# Setup logger for this instance
|
|
184
185
|
self.log = logger.bind(agent=name)
|
|
185
|
-
self.log.info("agent_initializing", route=route, host=host, port=port)
|
|
186
|
+
self.log.info("agent_initializing", agent=name, route=route, host=self.host, port=self.port)
|
|
186
187
|
|
|
187
188
|
# Store agent-specific parameters
|
|
188
189
|
self._default_webhook_url = default_webhook_url
|
|
@@ -64,7 +64,7 @@ class SWMLService:
|
|
|
64
64
|
name: str,
|
|
65
65
|
route: str = "/",
|
|
66
66
|
host: str = "0.0.0.0",
|
|
67
|
-
port: int =
|
|
67
|
+
port: Optional[int] = None,
|
|
68
68
|
basic_auth: Optional[Tuple[str, str]] = None,
|
|
69
69
|
schema_path: Optional[str] = None,
|
|
70
70
|
config_file: Optional[str] = None
|
|
@@ -84,7 +84,8 @@ class SWMLService:
|
|
|
84
84
|
self.name = name
|
|
85
85
|
self.route = route.rstrip("/") # Ensure no trailing slash
|
|
86
86
|
self.host = host
|
|
87
|
-
|
|
87
|
+
# Use provided port, or PORT env var, or default to 3000
|
|
88
|
+
self.port = port if port is not None else int(os.environ.get("PORT", 3000))
|
|
88
89
|
|
|
89
90
|
# Initialize logger for this instance FIRST before using it
|
|
90
91
|
self.log = logger.bind(service=name)
|
|
@@ -107,7 +108,7 @@ class SWMLService:
|
|
|
107
108
|
proxy_url_base=self._proxy_url_base)
|
|
108
109
|
self._proxy_detection_done = False
|
|
109
110
|
self._proxy_debug = os.environ.get('SWML_PROXY_DEBUG', '').lower() in ('true', '1', 'yes')
|
|
110
|
-
self.log.info("service_initializing", route=self.route, host=host, port=port)
|
|
111
|
+
self.log.info("service_initializing", route=self.route, host=self.host, port=self.port)
|
|
111
112
|
|
|
112
113
|
# Set basic auth credentials
|
|
113
114
|
if basic_auth is not None:
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
signalwire_agents/__init__.py,sha256=
|
|
2
|
-
signalwire_agents/agent_server.py,sha256=
|
|
1
|
+
signalwire_agents/__init__.py,sha256=4jSnai0mMz6X5lR9EegR-luqA2KbgndfD8NPg1FV7iw,5036
|
|
2
|
+
signalwire_agents/agent_server.py,sha256=abnGPGwmu7k8qGVBJDebhR7yZfr3clGk_WBcC50i7kc,32352
|
|
3
3
|
signalwire_agents/schema.json,sha256=YQv4-KiegE00XvxoLMKAml6aCGitnt3kBq31ECxTHK8,385886
|
|
4
4
|
signalwire_agents/agents/bedrock.py,sha256=J582gooNtxtep4xdVOfyDzRtHp_XrurPMS93xf2Xod0,10836
|
|
5
5
|
signalwire_agents/cli/__init__.py,sha256=XbxAQFaCIdGXIXJiriVBWoFPOJsC401u21588nO4TG8,388
|
|
6
6
|
signalwire_agents/cli/build_search.py,sha256=wDuonVY9fcqspThRHmbArTuZLKsiStu-ozVExtqawA8,54937
|
|
7
7
|
signalwire_agents/cli/config.py,sha256=2i4e0BArdKsaXxjeueYYRNke7GWicHPYC2wuitVrP7A,2541
|
|
8
|
-
signalwire_agents/cli/dokku.py,sha256=
|
|
8
|
+
signalwire_agents/cli/dokku.py,sha256=WuyqErWPZ-Wyqfxlzb3DyPhkyrSropUYzHtTuqzjGiM,80255
|
|
9
9
|
signalwire_agents/cli/init_project.py,sha256=nNBPni3x1xzdJ5pAZsQRUYK1t1e64NzwUUkq-Uc3mzo,78835
|
|
10
10
|
signalwire_agents/cli/swaig_test_wrapper.py,sha256=t63HQpEc1Up5AcysEHP1OsEQcgSMKH-9H1L2IhFso18,1533
|
|
11
11
|
signalwire_agents/cli/test_swaig.py,sha256=-v-XjTUWZNxmMJuOF5_cB1Jz8x8emJoqgqS_8jLeT4Y,31487
|
|
@@ -26,7 +26,7 @@ signalwire_agents/cli/simulation/data_generation.py,sha256=pxa9aJ6XkI0O8yAIGvBTU
|
|
|
26
26
|
signalwire_agents/cli/simulation/data_overrides.py,sha256=3_3pT6j-q2gRufPX2bZ1BrmY7u1IdloLooKAJil33vI,6319
|
|
27
27
|
signalwire_agents/cli/simulation/mock_env.py,sha256=fvaR_xdLMm8AbpNUbTJOFG9THcti3Zds-0QNDbKMaYk,10249
|
|
28
28
|
signalwire_agents/core/__init__.py,sha256=xjPq8DmUnWYUG28sd17n430VWPmMH9oZ9W14gYwG96g,806
|
|
29
|
-
signalwire_agents/core/agent_base.py,sha256=
|
|
29
|
+
signalwire_agents/core/agent_base.py,sha256=zZRykB8VD277_5_NZbYP7ZcpeqTdQ4yWlpNnLXOljEY,58883
|
|
30
30
|
signalwire_agents/core/auth_handler.py,sha256=jXrof9WZ1W9qqlQT9WElcmSRafL2kG7207x5SqWN9MU,8481
|
|
31
31
|
signalwire_agents/core/config_loader.py,sha256=rStVRRUaeMGrMc44ocr0diMQQARZhbKqwMqQ6kqUNos,8722
|
|
32
32
|
signalwire_agents/core/contexts.py,sha256=g9FgOGMfGCUWlm57YZcv7CvOf-Ub9FdKZIOMu14ADfE,24428
|
|
@@ -41,7 +41,7 @@ signalwire_agents/core/swaig_function.py,sha256=KnUQ2g99kDSzOzD1PJ0Iqs8DeeZ6jDII
|
|
|
41
41
|
signalwire_agents/core/swml_builder.py,sha256=tJBFDAVTENEfjGLp2h9_AKOYt5O9FrSYLI-nZZVwM1E,15604
|
|
42
42
|
signalwire_agents/core/swml_handler.py,sha256=hFDq41dQWL3EdFbq6h0hizE1dIqdVeiTeCrujbZsPzo,8397
|
|
43
43
|
signalwire_agents/core/swml_renderer.py,sha256=-WAB_5ss836a8nBo5zlb6SaQKFNF4XIo1odWIXM4eE8,6860
|
|
44
|
-
signalwire_agents/core/swml_service.py,sha256=
|
|
44
|
+
signalwire_agents/core/swml_service.py,sha256=TCsn3g_kAxz1DfSiPygpBv6KTd6EEDaWplFVqZZnZw8,49995
|
|
45
45
|
signalwire_agents/core/agent/__init__.py,sha256=qccTmLD9b24tZDAoIPEY6vJ2p1R_ArZ_ZCKbBlKvPQ8,239
|
|
46
46
|
signalwire_agents/core/agent/config/__init__.py,sha256=5XvTfnYeeGdoLr4tJjbe1OhF26nOcR5VTDIhtMGCu3I,244
|
|
47
47
|
signalwire_agents/core/agent/deployment/__init__.py,sha256=4Mr7baQ_xR_hfJ72YxQRAT_GFa663YjFX_PumJ35Xds,191
|
|
@@ -136,12 +136,12 @@ signalwire_agents/utils/token_generators.py,sha256=4Mr7baQ_xR_hfJ72YxQRAT_GFa663
|
|
|
136
136
|
signalwire_agents/utils/validators.py,sha256=4Mr7baQ_xR_hfJ72YxQRAT_GFa663YjFX_PumJ35Xds,191
|
|
137
137
|
signalwire_agents/web/__init__.py,sha256=XE_pSTY9Aalzr7J7wqFth1Zr3cccQHPPcF5HWNrOpz8,383
|
|
138
138
|
signalwire_agents/web/web_service.py,sha256=a2PSHJgX1tlZr0Iz1A1UouZjXEePJAZL632evvLVM38,21071
|
|
139
|
-
signalwire_agents-1.0.
|
|
140
|
-
signalwire_agents-1.0.
|
|
141
|
-
signalwire_agents-1.0.
|
|
142
|
-
signalwire_agents-1.0.
|
|
143
|
-
signalwire_agents-1.0.
|
|
144
|
-
signalwire_agents-1.0.
|
|
145
|
-
signalwire_agents-1.0.
|
|
146
|
-
signalwire_agents-1.0.
|
|
147
|
-
signalwire_agents-1.0.
|
|
139
|
+
signalwire_agents-1.0.16.dev1.data/data/share/man/man1/sw-agent-init.1,sha256=J4k5Oi74BnWCPCvsaw00vuyyqDPuIECjJIPu5OynvJc,8381
|
|
140
|
+
signalwire_agents-1.0.16.dev1.data/data/share/man/man1/sw-search.1,sha256=9jJ6V6t6DgmXByz8Lw9exjf683Cw3sJGro8-eB0M9EY,10413
|
|
141
|
+
signalwire_agents-1.0.16.dev1.data/data/share/man/man1/swaig-test.1,sha256=Ri0EITo8YMFowkcYltwPSwU4VJdRzo7XTWloi5WddCg,7815
|
|
142
|
+
signalwire_agents-1.0.16.dev1.dist-info/licenses/LICENSE,sha256=NYvAsB-rTcSvG9cqHt9EUHAWLiA9YzM4Qfz-mPdvDR0,1067
|
|
143
|
+
signalwire_agents-1.0.16.dev1.dist-info/METADATA,sha256=kBXRWmFVvb2ocI7ebmIJDb2HzsVztvf1W6GOfS01yJM,41745
|
|
144
|
+
signalwire_agents-1.0.16.dev1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
145
|
+
signalwire_agents-1.0.16.dev1.dist-info/entry_points.txt,sha256=fMiBH-GLeXGaWWn58Mcj7KM_m3SdomQMUQu-1LTqscw,315
|
|
146
|
+
signalwire_agents-1.0.16.dev1.dist-info/top_level.txt,sha256=kDGS6ZYv84K9P5Kyg9_S8P_pbUXoHkso0On_DB5bbWc,18
|
|
147
|
+
signalwire_agents-1.0.16.dev1.dist-info/RECORD,,
|
|
File without changes
|
{signalwire_agents-1.0.15.data → signalwire_agents-1.0.16.dev1.data}/data/share/man/man1/sw-search.1
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{signalwire_agents-1.0.15.dist-info → signalwire_agents-1.0.16.dev1.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{signalwire_agents-1.0.15.dist-info → signalwire_agents-1.0.16.dev1.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
{signalwire_agents-1.0.15.dist-info → signalwire_agents-1.0.16.dev1.dist-info}/top_level.txt
RENAMED
|
File without changes
|