squads-cli 0.2.0 → 0.4.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/README.md +470 -75
- package/dist/chunk-7OCVIDC7.js +12 -0
- package/dist/chunk-7OCVIDC7.js.map +1 -0
- package/dist/chunk-FUHBEL3L.js +203 -0
- package/dist/chunk-FUHBEL3L.js.map +1 -0
- package/dist/chunk-G63RBKDH.js +980 -0
- package/dist/chunk-G63RBKDH.js.map +1 -0
- package/dist/cli.js +4174 -1962
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -4
- package/dist/index.js +1 -11
- package/dist/index.js.map +1 -1
- package/dist/memory-4PVUKIDK.js +19 -0
- package/dist/memory-4PVUKIDK.js.map +1 -0
- package/dist/sessions-JCQ34BEU.js +15 -0
- package/dist/sessions-JCQ34BEU.js.map +1 -0
- package/docker/.env.example +17 -0
- package/docker/README.md +92 -0
- package/docker/docker-compose.engram.yml +289 -0
- package/docker/docker-compose.yml +194 -0
- package/docker/init-db.sql +399 -0
- package/docker/init-engram-db.sql +148 -0
- package/docker/init-langfuse-db.sh +10 -0
- package/docker/otel-collector.yaml +34 -0
- package/docker/squads-bridge/Dockerfile +14 -0
- package/docker/squads-bridge/Dockerfile.proxy +14 -0
- package/docker/squads-bridge/anthropic_proxy.py +313 -0
- package/docker/squads-bridge/requirements.txt +7 -0
- package/docker/squads-bridge/squads_bridge.py +1610 -0
- package/docker/telemetry-ping/Dockerfile +10 -0
- package/docker/telemetry-ping/deploy.sh +69 -0
- package/docker/telemetry-ping/main.py +136 -0
- package/docker/telemetry-ping/requirements.txt +3 -0
- package/package.json +15 -2
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
PROJECT_ID="inspired-answer-481202-f6"
|
|
5
|
+
REGION="us-central1"
|
|
6
|
+
SERVICE_NAME="squads-telemetry"
|
|
7
|
+
TOPIC_ID="squads-cli-telemetry"
|
|
8
|
+
DATASET_ID="telemetry"
|
|
9
|
+
TABLE_ID="cli_events"
|
|
10
|
+
|
|
11
|
+
echo "=== Squads Telemetry Deployment ==="
|
|
12
|
+
|
|
13
|
+
# 1. Enable APIs
|
|
14
|
+
echo "[1/6] Enabling APIs..."
|
|
15
|
+
gcloud services enable \
|
|
16
|
+
pubsub.googleapis.com \
|
|
17
|
+
bigquery.googleapis.com \
|
|
18
|
+
run.googleapis.com \
|
|
19
|
+
cloudbuild.googleapis.com \
|
|
20
|
+
--project=$PROJECT_ID
|
|
21
|
+
|
|
22
|
+
# 2. Create Pub/Sub topic
|
|
23
|
+
echo "[2/6] Creating Pub/Sub topic..."
|
|
24
|
+
gcloud pubsub topics create $TOPIC_ID --project=$PROJECT_ID 2>/dev/null || echo "Topic exists"
|
|
25
|
+
|
|
26
|
+
# 3. Create BigQuery dataset
|
|
27
|
+
echo "[3/6] Creating BigQuery dataset..."
|
|
28
|
+
bq --project_id=$PROJECT_ID mk --dataset $DATASET_ID 2>/dev/null || echo "Dataset exists"
|
|
29
|
+
|
|
30
|
+
# 4. Create BigQuery table
|
|
31
|
+
echo "[4/6] Creating BigQuery table..."
|
|
32
|
+
bq --project_id=$PROJECT_ID mk --table $DATASET_ID.$TABLE_ID \
|
|
33
|
+
event:STRING,timestamp:TIMESTAMP,anonymous_id:STRING,cli_version:STRING,properties:STRING,server_ts:TIMESTAMP,source:STRING \
|
|
34
|
+
2>/dev/null || echo "Table exists"
|
|
35
|
+
|
|
36
|
+
# 5. Create Pub/Sub → BigQuery subscription
|
|
37
|
+
echo "[5/6] Creating BigQuery subscription..."
|
|
38
|
+
gcloud pubsub subscriptions create ${TOPIC_ID}-bq \
|
|
39
|
+
--topic=$TOPIC_ID \
|
|
40
|
+
--bigquery-table=$PROJECT_ID:$DATASET_ID.$TABLE_ID \
|
|
41
|
+
--use-topic-schema=false \
|
|
42
|
+
--write-metadata \
|
|
43
|
+
--project=$PROJECT_ID 2>/dev/null || echo "Subscription exists"
|
|
44
|
+
|
|
45
|
+
# 6. Deploy Cloud Run
|
|
46
|
+
echo "[6/6] Deploying to Cloud Run..."
|
|
47
|
+
gcloud run deploy $SERVICE_NAME \
|
|
48
|
+
--source=. \
|
|
49
|
+
--region=$REGION \
|
|
50
|
+
--platform=managed \
|
|
51
|
+
--allow-unauthenticated \
|
|
52
|
+
--set-env-vars="GCP_PROJECT=$PROJECT_ID,PUBSUB_TOPIC=$TOPIC_ID" \
|
|
53
|
+
--memory=256Mi \
|
|
54
|
+
--min-instances=0 \
|
|
55
|
+
--max-instances=5 \
|
|
56
|
+
--project=$PROJECT_ID
|
|
57
|
+
|
|
58
|
+
# Get URL
|
|
59
|
+
URL=$(gcloud run services describe $SERVICE_NAME --region=$REGION --project=$PROJECT_ID --format='value(status.url)')
|
|
60
|
+
|
|
61
|
+
echo ""
|
|
62
|
+
echo "=== Deployment Complete ==="
|
|
63
|
+
echo "Endpoint: $URL/ping"
|
|
64
|
+
echo ""
|
|
65
|
+
echo "Test with:"
|
|
66
|
+
echo " curl -X POST $URL/ping -H 'Content-Type: application/json' -d '{\"event\":\"test\"}'"
|
|
67
|
+
echo ""
|
|
68
|
+
echo "Update squads-cli telemetry URL:"
|
|
69
|
+
echo " SQUADS_TELEMETRY_URL=$URL/ping"
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Squads CLI Telemetry Ping - Public endpoint for anonymous CLI telemetry.
|
|
3
|
+
Receives events and publishes to Pub/Sub for BigQuery streaming.
|
|
4
|
+
"""
|
|
5
|
+
import os
|
|
6
|
+
import json
|
|
7
|
+
import hashlib
|
|
8
|
+
import hmac
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from flask import Flask, request, jsonify
|
|
11
|
+
from google.cloud import pubsub_v1
|
|
12
|
+
|
|
13
|
+
app = Flask(__name__)
|
|
14
|
+
|
|
15
|
+
# Config
|
|
16
|
+
PROJECT_ID = os.environ.get("GCP_PROJECT", "inspired-answer-481202-f6")
|
|
17
|
+
TOPIC_ID = os.environ.get("PUBSUB_TOPIC", "squads-cli-telemetry")
|
|
18
|
+
DEBUG = os.environ.get("DEBUG", "0") == "1"
|
|
19
|
+
|
|
20
|
+
# API Key for validation (prevents spam/contamination)
|
|
21
|
+
# CLI embeds this key (obfuscated) - not true security, but adds friction
|
|
22
|
+
API_KEY = os.environ.get("TELEMETRY_API_KEY", "sq_tel_v1_7f8a9b2c3d4e5f6a")
|
|
23
|
+
|
|
24
|
+
# Valid event prefixes (reject unknown events)
|
|
25
|
+
VALID_EVENT_PREFIXES = ("cli.", "agent.", "squad.", "error.")
|
|
26
|
+
|
|
27
|
+
# Pub/Sub publisher (lazy init)
|
|
28
|
+
publisher = None
|
|
29
|
+
|
|
30
|
+
def get_publisher():
|
|
31
|
+
global publisher
|
|
32
|
+
if publisher is None:
|
|
33
|
+
publisher = pubsub_v1.PublisherClient()
|
|
34
|
+
return publisher
|
|
35
|
+
|
|
36
|
+
def validate_request() -> tuple[bool, str]:
|
|
37
|
+
"""Validate API key and basic structure."""
|
|
38
|
+
# Check API key header
|
|
39
|
+
api_key = request.headers.get("X-Squads-Key") or request.headers.get("Authorization", "").replace("Bearer ", "")
|
|
40
|
+
if not api_key or not hmac.compare_digest(api_key, API_KEY):
|
|
41
|
+
return False, "invalid_key"
|
|
42
|
+
return True, ""
|
|
43
|
+
|
|
44
|
+
def validate_event(event: dict) -> bool:
|
|
45
|
+
"""Validate event structure."""
|
|
46
|
+
if not isinstance(event, dict):
|
|
47
|
+
return False
|
|
48
|
+
|
|
49
|
+
event_name = event.get("event", "")
|
|
50
|
+
if not event_name or not any(event_name.startswith(p) for p in VALID_EVENT_PREFIXES):
|
|
51
|
+
return False
|
|
52
|
+
|
|
53
|
+
# Must have properties dict (can be empty)
|
|
54
|
+
if "properties" in event and not isinstance(event.get("properties"), dict):
|
|
55
|
+
return False
|
|
56
|
+
|
|
57
|
+
return True
|
|
58
|
+
|
|
59
|
+
def publish_event(event: dict) -> bool:
|
|
60
|
+
"""Publish event to Pub/Sub."""
|
|
61
|
+
try:
|
|
62
|
+
pub = get_publisher()
|
|
63
|
+
topic_path = pub.topic_path(PROJECT_ID, TOPIC_ID)
|
|
64
|
+
|
|
65
|
+
# Add server metadata
|
|
66
|
+
event["server_ts"] = datetime.utcnow().isoformat() + "Z"
|
|
67
|
+
event["source"] = "squads-cli"
|
|
68
|
+
|
|
69
|
+
data = json.dumps(event).encode("utf-8")
|
|
70
|
+
future = pub.publish(topic_path, data)
|
|
71
|
+
future.result(timeout=5) # Wait for ack
|
|
72
|
+
return True
|
|
73
|
+
except Exception as e:
|
|
74
|
+
if DEBUG:
|
|
75
|
+
print(f"Pub/Sub error: {e}")
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
@app.route("/", methods=["GET"])
|
|
79
|
+
def health():
|
|
80
|
+
"""Health check."""
|
|
81
|
+
return jsonify({"status": "ok", "service": "squads-telemetry"}), 200
|
|
82
|
+
|
|
83
|
+
@app.route("/ping", methods=["POST"])
|
|
84
|
+
def ping():
|
|
85
|
+
"""
|
|
86
|
+
Receive telemetry ping from CLI.
|
|
87
|
+
|
|
88
|
+
Headers:
|
|
89
|
+
X-Squads-Key: <api_key>
|
|
90
|
+
|
|
91
|
+
POST /ping
|
|
92
|
+
Content-Type: application/json
|
|
93
|
+
|
|
94
|
+
{"event": "cli.run", "properties": {"squad": "marketing", "durationMs": 1234}}
|
|
95
|
+
"""
|
|
96
|
+
# Validate API key
|
|
97
|
+
valid, error = validate_request()
|
|
98
|
+
if not valid:
|
|
99
|
+
return jsonify({"error": error}), 401
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
data = request.get_json(silent=True)
|
|
103
|
+
if not data:
|
|
104
|
+
return jsonify({"error": "invalid json"}), 400
|
|
105
|
+
|
|
106
|
+
# Support single event or batch
|
|
107
|
+
events = data.get("events", [data])
|
|
108
|
+
|
|
109
|
+
# Validate and publish
|
|
110
|
+
published = 0
|
|
111
|
+
rejected = 0
|
|
112
|
+
for event in events:
|
|
113
|
+
if validate_event(event):
|
|
114
|
+
if publish_event(event):
|
|
115
|
+
published += 1
|
|
116
|
+
else:
|
|
117
|
+
rejected += 1
|
|
118
|
+
|
|
119
|
+
return jsonify({
|
|
120
|
+
"status": "ok",
|
|
121
|
+
"received": len(events),
|
|
122
|
+
"published": published,
|
|
123
|
+
"rejected": rejected,
|
|
124
|
+
}), 200
|
|
125
|
+
|
|
126
|
+
except Exception as e:
|
|
127
|
+
return jsonify({"error": str(e)}), 500
|
|
128
|
+
|
|
129
|
+
@app.route("/ping", methods=["GET"])
|
|
130
|
+
def ping_get():
|
|
131
|
+
"""Simple GET ping for uptime checks."""
|
|
132
|
+
return jsonify({"pong": True, "ts": datetime.utcnow().isoformat()}), 200
|
|
133
|
+
|
|
134
|
+
if __name__ == "__main__":
|
|
135
|
+
port = int(os.environ.get("PORT", 8080))
|
|
136
|
+
app.run(host="0.0.0.0", port=port, debug=DEBUG)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "squads-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "A CLI for humans and agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -15,7 +15,17 @@
|
|
|
15
15
|
}
|
|
16
16
|
},
|
|
17
17
|
"files": [
|
|
18
|
-
"dist"
|
|
18
|
+
"dist",
|
|
19
|
+
"docker/docker-compose.yml",
|
|
20
|
+
"docker/docker-compose.engram.yml",
|
|
21
|
+
"docker/.env.example",
|
|
22
|
+
"docker/init-db.sql",
|
|
23
|
+
"docker/init-engram-db.sql",
|
|
24
|
+
"docker/init-langfuse-db.sh",
|
|
25
|
+
"docker/otel-collector.yaml",
|
|
26
|
+
"docker/README.md",
|
|
27
|
+
"docker/squads-bridge/",
|
|
28
|
+
"docker/telemetry-ping/"
|
|
19
29
|
],
|
|
20
30
|
"scripts": {
|
|
21
31
|
"build": "tsup",
|
|
@@ -56,6 +66,9 @@
|
|
|
56
66
|
},
|
|
57
67
|
"dependencies": {
|
|
58
68
|
"@supabase/supabase-js": "^2.89.0",
|
|
69
|
+
"@types/blessed": "^0.1.27",
|
|
70
|
+
"blessed": "^0.1.81",
|
|
71
|
+
"blessed-contrib": "^4.11.0",
|
|
59
72
|
"chalk": "^5.3.0",
|
|
60
73
|
"commander": "^12.1.0",
|
|
61
74
|
"dotenv": "^17.2.3",
|