@contextableai/openclaw-memory-graphiti 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.
@@ -0,0 +1,91 @@
1
+ {
2
+ "id": "memory-graphiti",
3
+ "kind": "memory",
4
+ "uiHints": {
5
+ "spicedb.endpoint": {
6
+ "label": "SpiceDB Endpoint",
7
+ "placeholder": "localhost:50051",
8
+ "help": "SpiceDB gRPC endpoint (host:port)"
9
+ },
10
+ "spicedb.token": {
11
+ "label": "SpiceDB Token",
12
+ "sensitive": true,
13
+ "placeholder": "dev_token",
14
+ "help": "SpiceDB pre-shared key (or use ${SPICEDB_TOKEN})"
15
+ },
16
+ "spicedb.insecure": {
17
+ "label": "Insecure Connection",
18
+ "help": "Allow insecure gRPC to localhost (dev mode)"
19
+ },
20
+ "graphiti.endpoint": {
21
+ "label": "Graphiti MCP Endpoint",
22
+ "placeholder": "http://localhost:8000",
23
+ "help": "Graphiti MCP server HTTP base URL"
24
+ },
25
+ "graphiti.defaultGroupId": {
26
+ "label": "Default Group ID",
27
+ "placeholder": "main",
28
+ "help": "Default Graphiti group_id for memory isolation"
29
+ },
30
+ "subjectType": {
31
+ "label": "Subject Type",
32
+ "placeholder": "agent",
33
+ "help": "SpiceDB subject object type (agent or person)",
34
+ "advanced": true
35
+ },
36
+ "subjectId": {
37
+ "label": "Subject ID",
38
+ "placeholder": "${OPENCLAW_AGENT_ID}",
39
+ "help": "SpiceDB subject ID for the current agent/person"
40
+ },
41
+ "autoCapture": {
42
+ "label": "Auto-Capture",
43
+ "help": "Automatically capture important information from conversations"
44
+ },
45
+ "autoRecall": {
46
+ "label": "Auto-Recall",
47
+ "help": "Automatically inject relevant memories into context"
48
+ },
49
+ "customInstructions": {
50
+ "label": "Custom Extraction Instructions",
51
+ "help": "Natural language rules for what Graphiti should extract from conversations",
52
+ "advanced": true
53
+ },
54
+ "maxCaptureMessages": {
55
+ "label": "Max Capture Messages",
56
+ "help": "Maximum number of recent messages to include in auto-capture (default: 10)",
57
+ "advanced": true
58
+ }
59
+ },
60
+ "configSchema": {
61
+ "type": "object",
62
+ "additionalProperties": false,
63
+ "properties": {
64
+ "spicedb": {
65
+ "type": "object",
66
+ "additionalProperties": false,
67
+ "properties": {
68
+ "endpoint": { "type": "string" },
69
+ "token": { "type": "string" },
70
+ "insecure": { "type": "boolean" }
71
+ },
72
+ "required": ["token"]
73
+ },
74
+ "graphiti": {
75
+ "type": "object",
76
+ "additionalProperties": false,
77
+ "properties": {
78
+ "endpoint": { "type": "string" },
79
+ "defaultGroupId": { "type": "string" }
80
+ }
81
+ },
82
+ "subjectType": { "type": "string", "enum": ["agent", "person"] },
83
+ "subjectId": { "type": "string" },
84
+ "autoCapture": { "type": "boolean" },
85
+ "autoRecall": { "type": "boolean" },
86
+ "customInstructions": { "type": "string" },
87
+ "maxCaptureMessages": { "type": "integer", "minimum": 1, "maximum": 50 }
88
+ },
89
+ "required": ["spicedb"]
90
+ }
91
+ }
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@contextableai/openclaw-memory-graphiti",
3
+ "version": "0.1.1",
4
+ "description": "OpenClaw two-layer memory plugin: SpiceDB authorization + Graphiti knowledge graph",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/Contextable/openclaw-memory-graphiti.git"
10
+ },
11
+ "keywords": [
12
+ "openclaw",
13
+ "memory",
14
+ "graphiti",
15
+ "spicedb",
16
+ "knowledge-graph",
17
+ "authorization"
18
+ ],
19
+ "files": [
20
+ "index.ts",
21
+ "config.ts",
22
+ "authorization.ts",
23
+ "graphiti.ts",
24
+ "search.ts",
25
+ "spicedb.ts",
26
+ "openclaw.plugin.json",
27
+ "schema.zed",
28
+ "docker/",
29
+ "scripts/"
30
+ ],
31
+ "scripts": {
32
+ "test": "vitest run",
33
+ "test:watch": "vitest",
34
+ "test:e2e": "OPENCLAW_LIVE_TEST=1 vitest run --config vitest.e2e.config.ts"
35
+ },
36
+ "dependencies": {
37
+ "@authzed/authzed-node": "^1.2.0",
38
+ "@sinclair/typebox": "0.34.48"
39
+ },
40
+ "devDependencies": {
41
+ "vitest": "^4.0.18"
42
+ },
43
+ "publishConfig": {
44
+ "access": "public"
45
+ },
46
+ "openclaw": {
47
+ "extensions": [
48
+ "./index.ts"
49
+ ]
50
+ }
51
+ }
package/schema.zed ADDED
@@ -0,0 +1,23 @@
1
+ definition person {}
2
+
3
+ definition agent {
4
+ relation owner: person
5
+ permission act_as = owner
6
+ }
7
+
8
+ definition group {
9
+ relation member: person | agent
10
+ permission access = member
11
+ permission contribute = member
12
+ }
13
+
14
+ definition memory_fragment {
15
+ relation source_group: group
16
+ relation involves: person | agent
17
+ relation shared_by: person | agent
18
+
19
+ // Can view if: directly involved, shared it, or have access to the source group
20
+ permission view = involves + shared_by + source_group->access
21
+ // Can delete if: you shared it (owner-level control)
22
+ permission delete = shared_by
23
+ }
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env bash
2
+ # -------------------------------------------------------------------
3
+ # dev-setup.sh — One-time installation of native dev services
4
+ #
5
+ # Installs:
6
+ # Redis 8.x from packages.redis.io (>= 7.2 required by FalkorDB)
7
+ # FalkorDB module prebuilt .so from GitHub releases
8
+ # SpiceDB prebuilt binary from GitHub releases
9
+ # Graphiti MCP cloned from GitHub, deps via uv
10
+ #
11
+ # Everything goes into .dev/ (project-local, gitignored) except
12
+ # Redis which is installed system-wide via apt.
13
+ #
14
+ # Requirements: curl, git, python3 (>= 3.10), sudo (for apt)
15
+ # -------------------------------------------------------------------
16
+ set -euo pipefail
17
+
18
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
19
+ PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
20
+ DEV_DIR="$PROJECT_DIR/.dev"
21
+
22
+ SPICEDB_VERSION="${SPICEDB_VERSION:-1.49.0}"
23
+ FALKORDB_VERSION="${FALKORDB_VERSION:-4.16.3}"
24
+
25
+ # Detect architecture
26
+ ARCH="$(uname -m)"
27
+ case "$ARCH" in
28
+ aarch64|arm64) ARCH_SUFFIX="arm64"; FALKOR_ARCH="arm64v8" ;;
29
+ x86_64|amd64) ARCH_SUFFIX="amd64"; FALKOR_ARCH="x64" ;;
30
+ *) echo "Unsupported architecture: $ARCH"; exit 1 ;;
31
+ esac
32
+
33
+ echo "==> Setting up dev environment in $DEV_DIR"
34
+ mkdir -p "$DEV_DIR/bin" "$DEV_DIR/lib" "$DEV_DIR/data/falkordb" "$DEV_DIR/logs" "$DEV_DIR/pids"
35
+
36
+ # -------------------------------------------------------------------
37
+ # 1. Redis (system-wide via apt — FalkorDB needs >= 7.2)
38
+ # -------------------------------------------------------------------
39
+ REDIS_OK=false
40
+ if command -v redis-server &>/dev/null; then
41
+ REDIS_MAJOR="$(redis-server --version | grep -oP 'v=\K[0-9]+')"
42
+ if [ "$REDIS_MAJOR" -ge 8 ] 2>/dev/null; then
43
+ REDIS_OK=true
44
+ echo "==> Redis already installed: $(redis-server --version | grep -oP 'v=\S+')"
45
+ fi
46
+ fi
47
+
48
+ if [ "$REDIS_OK" = false ]; then
49
+ echo "==> Installing Redis >= 7.2 from packages.redis.io..."
50
+ curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg 2>/dev/null
51
+ echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs 2>/dev/null || echo bookworm) main" \
52
+ | sudo tee /etc/apt/sources.list.d/redis.list >/dev/null
53
+ sudo apt-get update -qq 2>&1 | tail -1
54
+ sudo apt-get install -y -qq redis-server libgomp1 2>&1 | tail -3
55
+ echo " Installed: $(redis-server --version | grep -oP 'v=\S+')"
56
+ fi
57
+
58
+ # Ensure libgomp1 is present (FalkorDB dependency)
59
+ dpkg -l libgomp1 >/dev/null 2>&1 || sudo apt-get install -y -qq libgomp1
60
+
61
+ # -------------------------------------------------------------------
62
+ # 2. FalkorDB module (.so)
63
+ # -------------------------------------------------------------------
64
+ if [ -f "$DEV_DIR/lib/falkordb.so" ]; then
65
+ echo "==> FalkorDB module already downloaded"
66
+ else
67
+ echo "==> Downloading FalkorDB v${FALKORDB_VERSION} module (${FALKOR_ARCH})..."
68
+ curl -fsSL -o "$DEV_DIR/lib/falkordb.so" \
69
+ "https://github.com/FalkorDB/FalkorDB/releases/download/v${FALKORDB_VERSION}/falkordb-${FALKOR_ARCH}.so"
70
+ chmod +x "$DEV_DIR/lib/falkordb.so"
71
+ echo " Downloaded: $DEV_DIR/lib/falkordb.so"
72
+ fi
73
+
74
+ # -------------------------------------------------------------------
75
+ # 3. PostgreSQL (optional — persistent datastore for SpiceDB)
76
+ # -------------------------------------------------------------------
77
+ if command -v pg_isready &>/dev/null; then
78
+ echo "==> PostgreSQL already installed: $(psql --version 2>/dev/null | head -1)"
79
+ else
80
+ echo "==> Installing PostgreSQL..."
81
+ sudo apt-get update -qq 2>&1 | tail -1
82
+ sudo apt-get install -y -qq postgresql postgresql-client 2>&1 | tail -3
83
+ echo " Installed: $(psql --version 2>/dev/null | head -1)"
84
+ fi
85
+
86
+ # -------------------------------------------------------------------
87
+ # 4. SpiceDB
88
+ # -------------------------------------------------------------------
89
+ if [ -x "$DEV_DIR/bin/spicedb" ]; then
90
+ echo "==> SpiceDB already installed: $("$DEV_DIR/bin/spicedb" version 2>/dev/null || echo 'unknown')"
91
+ else
92
+ echo "==> Downloading SpiceDB v${SPICEDB_VERSION} (${ARCH_SUFFIX})..."
93
+ SPICEDB_URL="https://github.com/authzed/spicedb/releases/download/v${SPICEDB_VERSION}/spicedb_${SPICEDB_VERSION}_linux_${ARCH_SUFFIX}.tar.gz"
94
+ curl -fsSL "$SPICEDB_URL" | tar xz -C "$DEV_DIR/bin" spicedb
95
+ chmod +x "$DEV_DIR/bin/spicedb"
96
+ echo " Installed: $("$DEV_DIR/bin/spicedb" version 2>/dev/null || echo "v${SPICEDB_VERSION}")"
97
+ fi
98
+
99
+ # -------------------------------------------------------------------
100
+ # 5. uv (Python package manager)
101
+ # -------------------------------------------------------------------
102
+ if command -v uv &>/dev/null; then
103
+ echo "==> uv already installed: $(uv --version)"
104
+ elif [ -x "$HOME/.local/bin/uv" ]; then
105
+ echo "==> uv already installed at ~/.local/bin/uv"
106
+ export PATH="$HOME/.local/bin:$PATH"
107
+ else
108
+ echo "==> Installing uv (Python package manager)..."
109
+ curl -LsSf https://astral.sh/uv/install.sh | sh
110
+ export PATH="$HOME/.local/bin:$PATH"
111
+ echo " Installed: $(uv --version)"
112
+ fi
113
+
114
+ # -------------------------------------------------------------------
115
+ # 6. Graphiti MCP Server
116
+ # -------------------------------------------------------------------
117
+ GRAPHITI_DIR="$DEV_DIR/graphiti"
118
+ if [ -d "$GRAPHITI_DIR/mcp_server" ]; then
119
+ echo "==> Graphiti repo already cloned, pulling latest..."
120
+ git -C "$GRAPHITI_DIR" pull --ff-only 2>/dev/null || echo " (pull skipped — detached or dirty)"
121
+ else
122
+ echo "==> Cloning Graphiti repo..."
123
+ git clone --depth 1 https://github.com/getzep/graphiti.git "$GRAPHITI_DIR"
124
+ fi
125
+
126
+ echo "==> Installing Graphiti MCP server dependencies..."
127
+ cd "$GRAPHITI_DIR/mcp_server"
128
+ if command -v uv &>/dev/null; then
129
+ uv sync 2>&1 | tail -3
130
+ else
131
+ "$HOME/.local/bin/uv" sync 2>&1 | tail -3
132
+ fi
133
+ cd "$PROJECT_DIR"
134
+
135
+ # -------------------------------------------------------------------
136
+ # Done
137
+ # -------------------------------------------------------------------
138
+ echo ""
139
+ echo "==> Dev environment ready!"
140
+ echo ""
141
+ echo " Redis: $(redis-server --version | grep -oP 'v=\S+')"
142
+ echo " FalkorDB: $DEV_DIR/lib/falkordb.so (v${FALKORDB_VERSION})"
143
+ echo " SpiceDB: $DEV_DIR/bin/spicedb"
144
+ echo " Graphiti: $GRAPHITI_DIR/mcp_server/"
145
+ echo ""
146
+ echo " Next: run ./scripts/dev-start.sh to start services"
@@ -0,0 +1,236 @@
1
+ #!/usr/bin/env bash
2
+ # -------------------------------------------------------------------
3
+ # dev-start.sh — Start FalkorDB + SpiceDB + Graphiti MCP server
4
+ #
5
+ # Services run in the background; logs go to .dev/logs/.
6
+ # PID files are written to .dev/pids/.
7
+ #
8
+ # Environment variables (all optional):
9
+ # OPENAI_API_KEY Required by Graphiti for entity extraction
10
+ # SPICEDB_TOKEN Pre-shared key (default: dev_token)
11
+ # SPICEDB_PORT gRPC port (default: 50051)
12
+ # SPICEDB_DATASTORE "memory" (default) or "postgres"
13
+ # SPICEDB_DB_URI Postgres connection URI (when SPICEDB_DATASTORE=postgres)
14
+ # FALKORDB_PORT Redis port (default: 6379)
15
+ # GRAPHITI_PORT HTTP port (default: 8000)
16
+ # -------------------------------------------------------------------
17
+ set -euo pipefail
18
+
19
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
20
+ PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
21
+ DEV_DIR="$PROJECT_DIR/.dev"
22
+
23
+ # Load .env if present
24
+ if [ -f "$PROJECT_DIR/.env" ]; then
25
+ set -a
26
+ # shellcheck disable=SC1091
27
+ source "$PROJECT_DIR/.env"
28
+ set +a
29
+ fi
30
+
31
+ SPICEDB_TOKEN="${SPICEDB_TOKEN:-dev_token}"
32
+ SPICEDB_PORT="${SPICEDB_PORT:-50051}"
33
+ SPICEDB_DATASTORE="${SPICEDB_DATASTORE:-memory}"
34
+ SPICEDB_DB_URI="${SPICEDB_DB_URI:-postgres://spicedb:spicedb_dev@127.0.0.1:5432/spicedb?sslmode=disable}"
35
+ FALKORDB_PORT="${FALKORDB_PORT:-6379}"
36
+ GRAPHITI_PORT="${GRAPHITI_PORT:-8000}"
37
+
38
+ mkdir -p "$DEV_DIR/logs" "$DEV_DIR/pids" "$DEV_DIR/data/falkordb"
39
+
40
+ # Ensure uv is on PATH
41
+ export PATH="$HOME/.local/bin:$DEV_DIR/bin:$PATH"
42
+
43
+ # -------------------------------------------------------------------
44
+ # Helper: check if a process is already running
45
+ # -------------------------------------------------------------------
46
+ is_running() {
47
+ local pidfile="$1"
48
+ if [ -f "$pidfile" ]; then
49
+ local pid
50
+ pid=$(cat "$pidfile")
51
+ if kill -0 "$pid" 2>/dev/null; then
52
+ return 0
53
+ fi
54
+ rm -f "$pidfile"
55
+ fi
56
+ return 1
57
+ }
58
+
59
+ # -------------------------------------------------------------------
60
+ # 1. FalkorDB (Redis + module)
61
+ # -------------------------------------------------------------------
62
+ if is_running "$DEV_DIR/pids/falkordb.pid"; then
63
+ echo "==> FalkorDB already running (pid $(cat "$DEV_DIR/pids/falkordb.pid"))"
64
+ else
65
+ echo "==> Starting FalkorDB on port $FALKORDB_PORT..."
66
+ redis-server \
67
+ --loadmodule "$DEV_DIR/lib/falkordb.so" \
68
+ --port "$FALKORDB_PORT" \
69
+ --dir "$DEV_DIR/data/falkordb" \
70
+ --daemonize no \
71
+ --save "" \
72
+ --appendonly no \
73
+ --loglevel notice \
74
+ > "$DEV_DIR/logs/falkordb.log" 2>&1 &
75
+ echo $! > "$DEV_DIR/pids/falkordb.pid"
76
+ echo " PID: $(cat "$DEV_DIR/pids/falkordb.pid") — log: .dev/logs/falkordb.log"
77
+
78
+ # Wait for FalkorDB to be ready
79
+ echo -n " Waiting for FalkorDB..."
80
+ for i in $(seq 1 30); do
81
+ if redis-cli -p "$FALKORDB_PORT" PING 2>/dev/null | grep -q PONG; then
82
+ echo " ready!"
83
+ break
84
+ fi
85
+ if [ "$i" -eq 30 ]; then
86
+ echo " timeout! Check .dev/logs/falkordb.log"
87
+ exit 1
88
+ fi
89
+ sleep 0.5
90
+ echo -n "."
91
+ done
92
+ fi
93
+
94
+ # -------------------------------------------------------------------
95
+ # 2. PostgreSQL (when SPICEDB_DATASTORE=postgres)
96
+ # -------------------------------------------------------------------
97
+ if [ "$SPICEDB_DATASTORE" = "postgres" ]; then
98
+ if pg_isready -q 2>/dev/null; then
99
+ echo "==> PostgreSQL already running"
100
+ else
101
+ echo "==> Starting PostgreSQL..."
102
+ sudo pg_ctlcluster 15 main start 2>&1
103
+ echo -n " Waiting for PostgreSQL..."
104
+ for i in $(seq 1 20); do
105
+ if pg_isready -q 2>/dev/null; then
106
+ echo " ready!"
107
+ break
108
+ fi
109
+ if [ "$i" -eq 20 ]; then
110
+ echo " timeout!"
111
+ exit 1
112
+ fi
113
+ sleep 0.5
114
+ echo -n "."
115
+ done
116
+ fi
117
+
118
+ # Ensure database and user exist (idempotent)
119
+ sudo su - postgres -c "psql -tc \"SELECT 1 FROM pg_roles WHERE rolname='spicedb'\" | grep -q 1" 2>/dev/null \
120
+ || sudo su - postgres -c "psql -c \"CREATE USER spicedb WITH PASSWORD 'spicedb_dev';\"" 2>/dev/null
121
+ sudo su - postgres -c "psql -tc \"SELECT 1 FROM pg_database WHERE datname='spicedb'\" | grep -q 1" 2>/dev/null \
122
+ || sudo su - postgres -c "psql -c \"CREATE DATABASE spicedb OWNER spicedb;\"" 2>/dev/null
123
+
124
+ # Run SpiceDB migrations
125
+ echo "==> Running SpiceDB migrations..."
126
+ "$DEV_DIR/bin/spicedb" migrate head \
127
+ --datastore-engine=postgres \
128
+ "--datastore-conn-uri=$SPICEDB_DB_URI" \
129
+ > "$DEV_DIR/logs/spicedb-migrate.log" 2>&1
130
+ echo " Migrations complete"
131
+ fi
132
+
133
+ # -------------------------------------------------------------------
134
+ # 3. SpiceDB
135
+ # -------------------------------------------------------------------
136
+ if is_running "$DEV_DIR/pids/spicedb.pid"; then
137
+ echo "==> SpiceDB already running (pid $(cat "$DEV_DIR/pids/spicedb.pid"))"
138
+ else
139
+ SPICEDB_ARGS=(
140
+ serve
141
+ "--grpc-preshared-key=$SPICEDB_TOKEN"
142
+ "--grpc-addr=:$SPICEDB_PORT"
143
+ "--datastore-engine=$SPICEDB_DATASTORE"
144
+ --http-enabled=true
145
+ )
146
+ if [ "$SPICEDB_DATASTORE" = "postgres" ]; then
147
+ SPICEDB_ARGS+=("--datastore-conn-uri=$SPICEDB_DB_URI")
148
+ fi
149
+
150
+ echo "==> Starting SpiceDB on port $SPICEDB_PORT (datastore: $SPICEDB_DATASTORE)..."
151
+ "$DEV_DIR/bin/spicedb" "${SPICEDB_ARGS[@]}" \
152
+ > "$DEV_DIR/logs/spicedb.log" 2>&1 &
153
+ echo $! > "$DEV_DIR/pids/spicedb.pid"
154
+ echo " PID: $(cat "$DEV_DIR/pids/spicedb.pid") — log: .dev/logs/spicedb.log"
155
+
156
+ # Wait for SpiceDB to be ready
157
+ echo -n " Waiting for SpiceDB..."
158
+ for i in $(seq 1 30); do
159
+ if curl -sf http://localhost:8443/healthz >/dev/null 2>&1; then
160
+ echo " ready!"
161
+ break
162
+ fi
163
+ if [ "$i" -eq 30 ]; then
164
+ echo " timeout! Check .dev/logs/spicedb.log"
165
+ exit 1
166
+ fi
167
+ sleep 0.5
168
+ echo -n "."
169
+ done
170
+ fi
171
+
172
+ # -------------------------------------------------------------------
173
+ # 4. Graphiti MCP Server (FalkorDB backend)
174
+ # -------------------------------------------------------------------
175
+ if is_running "$DEV_DIR/pids/graphiti.pid"; then
176
+ echo "==> Graphiti MCP server already running (pid $(cat "$DEV_DIR/pids/graphiti.pid"))"
177
+ else
178
+ if [ -z "${OPENAI_API_KEY:-}" ]; then
179
+ echo ""
180
+ echo "WARNING: OPENAI_API_KEY is not set."
181
+ echo "Graphiti needs it for entity extraction and embeddings."
182
+ echo "Set it in .env or export it before running this script."
183
+ echo ""
184
+ fi
185
+
186
+ echo "==> Starting Graphiti MCP server on port $GRAPHITI_PORT..."
187
+
188
+ GRAPHITI_DIR="$DEV_DIR/graphiti/mcp_server"
189
+
190
+ # Set environment for Graphiti
191
+ export OPENAI_API_KEY="${OPENAI_API_KEY:-}"
192
+ export FALKORDB_URI="redis://localhost:$FALKORDB_PORT"
193
+
194
+ cd "$GRAPHITI_DIR"
195
+ uv run main.py \
196
+ --transport http \
197
+ --port "$GRAPHITI_PORT" \
198
+ > "$DEV_DIR/logs/graphiti.log" 2>&1 &
199
+ echo $! > "$DEV_DIR/pids/graphiti.pid"
200
+ cd "$PROJECT_DIR"
201
+
202
+ echo " PID: $(cat "$DEV_DIR/pids/graphiti.pid") — log: .dev/logs/graphiti.log"
203
+
204
+ # Wait for Graphiti to be ready
205
+ echo -n " Waiting for Graphiti..."
206
+ for i in $(seq 1 60); do
207
+ if curl -sf "http://localhost:$GRAPHITI_PORT/health" >/dev/null 2>&1; then
208
+ echo " ready!"
209
+ break
210
+ fi
211
+ if [ "$i" -eq 60 ]; then
212
+ echo " timeout! Check .dev/logs/graphiti.log"
213
+ exit 1
214
+ fi
215
+ sleep 1
216
+ echo -n "."
217
+ done
218
+ fi
219
+
220
+ # -------------------------------------------------------------------
221
+ # Summary
222
+ # -------------------------------------------------------------------
223
+ echo ""
224
+ echo "==> All services running:"
225
+ echo " FalkorDB: localhost:$FALKORDB_PORT (Redis protocol)"
226
+ echo " SpiceDB gRPC: localhost:$SPICEDB_PORT (datastore: $SPICEDB_DATASTORE)"
227
+ echo " SpiceDB HTTP: localhost:8443"
228
+ echo " Graphiti MCP: http://localhost:$GRAPHITI_PORT"
229
+ echo " Graphiti health: http://localhost:$GRAPHITI_PORT/health"
230
+ echo ""
231
+ echo " Stop with: ./scripts/dev-stop.sh"
232
+ echo " Status: ./scripts/dev-status.sh"
233
+ echo " Logs: tail -f .dev/logs/*.log"
234
+ echo ""
235
+ echo " Run e2e tests:"
236
+ echo " OPENCLAW_LIVE_TEST=1 pnpm vitest run e2e.test.ts"
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env bash
2
+ # -------------------------------------------------------------------
3
+ # dev-status.sh — Check status of dev services
4
+ # -------------------------------------------------------------------
5
+ set -euo pipefail
6
+
7
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
8
+ DEV_DIR="$(cd "$SCRIPT_DIR/.." && pwd)/.dev"
9
+
10
+ RED='\033[0;31m'
11
+ GREEN='\033[0;32m'
12
+ NC='\033[0m' # No Color
13
+
14
+ check_service() {
15
+ local name="$1"
16
+ local pidfile="$DEV_DIR/pids/${name}.pid"
17
+ local health_url="$2"
18
+
19
+ printf " %-20s" "$name:"
20
+
21
+ # Check PID
22
+ if [ -f "$pidfile" ]; then
23
+ local pid
24
+ pid=$(cat "$pidfile")
25
+ if kill -0 "$pid" 2>/dev/null; then
26
+ printf "${GREEN}running${NC} (pid %s)" "$pid"
27
+ else
28
+ printf "${RED}dead${NC} (stale pid %s)" "$pid"
29
+ rm -f "$pidfile"
30
+ echo ""
31
+ return
32
+ fi
33
+ else
34
+ printf "${RED}stopped${NC}"
35
+ echo ""
36
+ return
37
+ fi
38
+
39
+ # Check health endpoint
40
+ if [ -n "$health_url" ]; then
41
+ if curl -sf "$health_url" >/dev/null 2>&1; then
42
+ printf " ${GREEN}healthy${NC}"
43
+ else
44
+ printf " ${RED}unhealthy${NC}"
45
+ fi
46
+ fi
47
+
48
+ echo ""
49
+ }
50
+
51
+ echo ""
52
+ echo "Dev service status:"
53
+ echo ""
54
+ check_service "falkordb" ""
55
+ check_service "spicedb" "http://localhost:8443/healthz"
56
+ check_service "graphiti" "http://localhost:8000/health"
57
+ echo ""
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env bash
2
+ # -------------------------------------------------------------------
3
+ # dev-stop.sh — Stop all dev services
4
+ # -------------------------------------------------------------------
5
+ set -euo pipefail
6
+
7
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
8
+ DEV_DIR="$(cd "$SCRIPT_DIR/.." && pwd)/.dev"
9
+
10
+ stop_service() {
11
+ local name="$1"
12
+ local pidfile="$DEV_DIR/pids/${name}.pid"
13
+
14
+ if [ -f "$pidfile" ]; then
15
+ local pid
16
+ pid=$(cat "$pidfile")
17
+ if kill -0 "$pid" 2>/dev/null; then
18
+ echo "==> Stopping $name (pid $pid)..."
19
+ kill "$pid"
20
+ # Wait up to 5 seconds for graceful shutdown
21
+ for _ in $(seq 1 10); do
22
+ if ! kill -0 "$pid" 2>/dev/null; then
23
+ break
24
+ fi
25
+ sleep 0.5
26
+ done
27
+ # Force kill if still running
28
+ if kill -0 "$pid" 2>/dev/null; then
29
+ echo " Force-killing $name..."
30
+ kill -9 "$pid" 2>/dev/null || true
31
+ fi
32
+ echo " $name stopped."
33
+ else
34
+ echo "==> $name not running (stale pid file)."
35
+ fi
36
+ rm -f "$pidfile"
37
+ else
38
+ echo "==> $name not running (no pid file)."
39
+ fi
40
+ }
41
+
42
+ stop_service "graphiti"
43
+ stop_service "spicedb"
44
+ stop_service "falkordb"
45
+
46
+ echo ""
47
+ echo "==> All services stopped."