dreaming-memory 0.2.0__tar.gz
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.
- dreaming_memory-0.2.0/.env.example +19 -0
- dreaming_memory-0.2.0/.gitignore +12 -0
- dreaming_memory-0.2.0/PKG-INFO +110 -0
- dreaming_memory-0.2.0/README.md +55 -0
- dreaming_memory-0.2.0/deploy/cloudflare/utrecht-dataos-api.worker.js +53 -0
- dreaming_memory-0.2.0/deploy/fleet/README.md +53 -0
- dreaming_memory-0.2.0/deploy/fleet/agent-memory-dashboard.service +14 -0
- dreaming_memory-0.2.0/deploy/fleet/agent-memory-eval.service +10 -0
- dreaming_memory-0.2.0/deploy/fleet/agent-memory-eval.timer +11 -0
- dreaming_memory-0.2.0/deploy/fleet/cloudflare_tunnel.py +114 -0
- dreaming_memory-0.2.0/deploy/fleet/install-agent-memory.sh +23 -0
- dreaming_memory-0.2.0/deploy/fleet/install-memory.sh +51 -0
- dreaming_memory-0.2.0/deploy/fleet/sync-env.sh +35 -0
- dreaming_memory-0.2.0/deploy/oci/README.md +98 -0
- dreaming_memory-0.2.0/deploy/oci/docker-compose.yml +21 -0
- dreaming_memory-0.2.0/deploy/oci/prefect_flow.py +74 -0
- dreaming_memory-0.2.0/examples/linear_memory_flow.py +66 -0
- dreaming_memory-0.2.0/pyproject.toml +96 -0
- dreaming_memory-0.2.0/src/dreaming_memory/__init__.py +18 -0
- dreaming_memory-0.2.0/src/dreaming_memory/agent_memory.py +161 -0
- dreaming_memory-0.2.0/src/dreaming_memory/cli.py +262 -0
- dreaming_memory-0.2.0/src/dreaming_memory/config.py +130 -0
- dreaming_memory-0.2.0/src/dreaming_memory/dashboard.py +150 -0
- dreaming_memory-0.2.0/src/dreaming_memory/integrations/__init__.py +13 -0
- dreaming_memory-0.2.0/src/dreaming_memory/integrations/cloudflare.py +62 -0
- dreaming_memory-0.2.0/src/dreaming_memory/integrations/linear.py +301 -0
- dreaming_memory-0.2.0/src/dreaming_memory/integrations/notion.py +142 -0
- dreaming_memory-0.2.0/src/dreaming_memory/integrations/slack.py +87 -0
- dreaming_memory-0.2.0/src/dreaming_memory/observability/__init__.py +4 -0
- dreaming_memory-0.2.0/src/dreaming_memory/observability/sentry.py +58 -0
- dreaming_memory-0.2.0/src/dreaming_memory/py.typed +1 -0
- dreaming_memory-0.2.0/src/dreaming_memory/semantic/__init__.py +3 -0
- dreaming_memory-0.2.0/src/dreaming_memory/semantic/lancedb.py +74 -0
- dreaming_memory-0.2.0/src/dreaming_memory/session.py +86 -0
- dreaming_memory-0.2.0/src/dreaming_memory/store/__init__.py +3 -0
- dreaming_memory-0.2.0/src/dreaming_memory/store/migrations/schema_v1.sql +39 -0
- dreaming_memory-0.2.0/src/dreaming_memory/store/migrations/schema_v2.sql +13 -0
- dreaming_memory-0.2.0/src/dreaming_memory/store/migrations/schema_v3.sql +47 -0
- dreaming_memory-0.2.0/src/dreaming_memory/store/oci.py +51 -0
- dreaming_memory-0.2.0/src/dreaming_memory/store/postgres.py +282 -0
- dreaming_memory-0.2.0/src/dreaming_memory/store/schema.sql +39 -0
- dreaming_memory-0.2.0/src/dreaming_memory/triage.py +175 -0
- dreaming_memory-0.2.0/src/dreaming_memory/types.py +64 -0
- dreaming_memory-0.2.0/tests/test_export.py +35 -0
- dreaming_memory-0.2.0/tests/test_memory.py +96 -0
- dreaming_memory-0.2.0/tests/test_slack.py +68 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Agent memory — development defaults (copy to .env, never commit secrets)
|
|
2
|
+
|
|
3
|
+
# Postgres SSOT (required)
|
|
4
|
+
AGENT_MEMORY_DATABASE_URL=postgresql://postgres:postgres@localhost:5432/agent_memory
|
|
5
|
+
|
|
6
|
+
# Linear (same keys as utrecht-data-os)
|
|
7
|
+
# LINEAR_API_KEY=lin_api_...
|
|
8
|
+
# LINEAR_TEAM_ID=
|
|
9
|
+
# LINEAR_TEAM_KEY=GROEP
|
|
10
|
+
|
|
11
|
+
# Notion
|
|
12
|
+
# NOTION_API_KEY=secret_...
|
|
13
|
+
# NOTION_TOKEN=secret_...
|
|
14
|
+
|
|
15
|
+
# Slack (incoming webhook for eval reports)
|
|
16
|
+
SLACK_WEBHOOK_URL=
|
|
17
|
+
|
|
18
|
+
# Optional LanceDB (local path or s3:// for R2)
|
|
19
|
+
# LANCE_DB_URI=./data/lancedb
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dreaming-memory
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Agent-agnostic memory extension — Postgres-backed agent memory with Linear, Notion, and Cloudflare integrations
|
|
5
|
+
Project-URL: Homepage, https://github.com/OnlineChefGroep/cursor-dreaming-sdk
|
|
6
|
+
Project-URL: Documentation, https://github.com/OnlineChefGroep/cursor-dreaming-sdk/tree/main/docs
|
|
7
|
+
Project-URL: Repository, https://github.com/OnlineChefGroep/cursor-dreaming-sdk
|
|
8
|
+
Project-URL: Issues, https://github.com/OnlineChefGroep/cursor-dreaming-sdk/issues
|
|
9
|
+
Author: OnlineChefGroep
|
|
10
|
+
License: MIT
|
|
11
|
+
Keywords: agent-memory,dream-eval,linear,notion,postgres,postgrest
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Classifier: Topic :: System :: Monitoring
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: <3.15,>=3.11
|
|
23
|
+
Requires-Dist: httpx>=0.27
|
|
24
|
+
Requires-Dist: psycopg-pool>=3.1
|
|
25
|
+
Requires-Dist: psycopg[binary]>=3.2
|
|
26
|
+
Requires-Dist: pydantic>=2.13
|
|
27
|
+
Provides-Extra: all
|
|
28
|
+
Requires-Dist: boto3>=1.35; extra == 'all'
|
|
29
|
+
Requires-Dist: fastapi>=0.115; extra == 'all'
|
|
30
|
+
Requires-Dist: lancedb>=0.22; extra == 'all'
|
|
31
|
+
Requires-Dist: pyarrow>=18.0; extra == 'all'
|
|
32
|
+
Requires-Dist: sentry-sdk>=2.19; extra == 'all'
|
|
33
|
+
Requires-Dist: upstash-redis>=1.2; extra == 'all'
|
|
34
|
+
Requires-Dist: uvicorn[standard]>=0.30; extra == 'all'
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: hypothesis>=6.100; extra == 'dev'
|
|
37
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
38
|
+
Requires-Dist: pyyaml>=6.0; extra == 'dev'
|
|
39
|
+
Requires-Dist: ruff>=0.15; extra == 'dev'
|
|
40
|
+
Provides-Extra: prefect
|
|
41
|
+
Requires-Dist: prefect>=3.0; extra == 'prefect'
|
|
42
|
+
Provides-Extra: r2
|
|
43
|
+
Requires-Dist: boto3>=1.35; extra == 'r2'
|
|
44
|
+
Provides-Extra: redis
|
|
45
|
+
Requires-Dist: upstash-redis>=1.2; extra == 'redis'
|
|
46
|
+
Provides-Extra: semantic
|
|
47
|
+
Requires-Dist: lancedb>=0.22; extra == 'semantic'
|
|
48
|
+
Requires-Dist: pyarrow>=18.0; extra == 'semantic'
|
|
49
|
+
Provides-Extra: sentry
|
|
50
|
+
Requires-Dist: sentry-sdk>=2.19; extra == 'sentry'
|
|
51
|
+
Provides-Extra: web
|
|
52
|
+
Requires-Dist: fastapi>=0.115; extra == 'web'
|
|
53
|
+
Requires-Dist: uvicorn[standard]>=0.30; extra == 'web'
|
|
54
|
+
Description-Content-Type: text/markdown
|
|
55
|
+
|
|
56
|
+
# cursor-dreaming-memory
|
|
57
|
+
|
|
58
|
+
Lightweight agent memory extension for **cursor-dreaming-sdk** (Utrecht Data OS / CHEF-308).
|
|
59
|
+
|
|
60
|
+
## Install
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
cd python
|
|
64
|
+
uv sync --extra dev
|
|
65
|
+
uv run dream-memory init # requires Postgres
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Optional semantic layer:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
uv sync --extra semantic
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Quick start
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from cursor_dreaming_memory import AgentMemory, SessionContext
|
|
78
|
+
from cursor_dreaming_memory.types import MemoryType, MemorySource
|
|
79
|
+
|
|
80
|
+
memory = AgentMemory()
|
|
81
|
+
memory.ensure_schema()
|
|
82
|
+
|
|
83
|
+
ctx = SessionContext.for_dream_eval("2026-06-15T09-00-00Z")
|
|
84
|
+
memory.remember(ctx, MemoryType.OBSERVATION, {"faithfulness": 0.63}, source=MemorySource.SDK)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## CLI
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
dream-memory init
|
|
91
|
+
dream-memory remember --session-id run-1 --content '{"note":"hello"}'
|
|
92
|
+
dream-memory recall --session-id run-1
|
|
93
|
+
dream-memory linear-ingest CHEF-308
|
|
94
|
+
dream-memory notion-ingest <page_id>
|
|
95
|
+
dream-memory export --session-id run-1 --output run-1.md
|
|
96
|
+
dream-memory slack-report --run-id run-1 --metrics-json metrics.json
|
|
97
|
+
dream-memory doctor
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- `export` writes a session's memory records as Markdown (`--output` to a file, or stdout).
|
|
101
|
+
- `slack-report` posts an eval report to Slack via `SLACK_WEBHOOK_URL` (no-op if unset).
|
|
102
|
+
- `doctor` reports config presence plus Postgres, Linear, and Notion connectivity.
|
|
103
|
+
|
|
104
|
+
## Example flow
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
uv run python examples/linear_memory_flow.py --issue CHEF-308 --comment
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
See [docs/agent-memory.md](../docs/agent-memory.md) for architecture and OCI deployment.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# cursor-dreaming-memory
|
|
2
|
+
|
|
3
|
+
Lightweight agent memory extension for **cursor-dreaming-sdk** (Utrecht Data OS / CHEF-308).
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
cd python
|
|
9
|
+
uv sync --extra dev
|
|
10
|
+
uv run dream-memory init # requires Postgres
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Optional semantic layer:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
uv sync --extra semantic
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
from cursor_dreaming_memory import AgentMemory, SessionContext
|
|
23
|
+
from cursor_dreaming_memory.types import MemoryType, MemorySource
|
|
24
|
+
|
|
25
|
+
memory = AgentMemory()
|
|
26
|
+
memory.ensure_schema()
|
|
27
|
+
|
|
28
|
+
ctx = SessionContext.for_dream_eval("2026-06-15T09-00-00Z")
|
|
29
|
+
memory.remember(ctx, MemoryType.OBSERVATION, {"faithfulness": 0.63}, source=MemorySource.SDK)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## CLI
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
dream-memory init
|
|
36
|
+
dream-memory remember --session-id run-1 --content '{"note":"hello"}'
|
|
37
|
+
dream-memory recall --session-id run-1
|
|
38
|
+
dream-memory linear-ingest CHEF-308
|
|
39
|
+
dream-memory notion-ingest <page_id>
|
|
40
|
+
dream-memory export --session-id run-1 --output run-1.md
|
|
41
|
+
dream-memory slack-report --run-id run-1 --metrics-json metrics.json
|
|
42
|
+
dream-memory doctor
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
- `export` writes a session's memory records as Markdown (`--output` to a file, or stdout).
|
|
46
|
+
- `slack-report` posts an eval report to Slack via `SLACK_WEBHOOK_URL` (no-op if unset).
|
|
47
|
+
- `doctor` reports config presence plus Postgres, Linear, and Notion connectivity.
|
|
48
|
+
|
|
49
|
+
## Example flow
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
uv run python examples/linear_memory_flow.py --issue CHEF-308 --comment
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
See [docs/agent-memory.md](../docs/agent-memory.md) for architecture and OCI deployment.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// utrecht-dataos-control-plane-api (Cloudflare Worker, custom domain api.chefgroep.online)
|
|
2
|
+
// Placeholder control-plane API surface + reverse proxy for the agent-memory dashboard.
|
|
3
|
+
// Bindings (kept on deploy via keep_bindings): UPSTASH_REDIS_REST_URL, UPSTASH_REDIS_REST_TOKEN
|
|
4
|
+
|
|
5
|
+
const DASHBOARD_ORIGIN = 'https://memory.chefgroep.online';
|
|
6
|
+
|
|
7
|
+
addEventListener('fetch', (event) => {
|
|
8
|
+
event.respondWith(handle(event.request));
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
async function handle(request) {
|
|
12
|
+
const url = new URL(request.url);
|
|
13
|
+
const path = url.pathname;
|
|
14
|
+
|
|
15
|
+
// Dashboard, served under the online API at /dashboard
|
|
16
|
+
if (path === '/dashboard' || path === '/dashboard/' || path.startsWith('/dashboard/')) {
|
|
17
|
+
const sub = path.length > '/dashboard'.length ? path.slice('/dashboard'.length) : '/';
|
|
18
|
+
return proxy(DASHBOARD_ORIGIN + sub + url.search, request);
|
|
19
|
+
}
|
|
20
|
+
// Footer links / refresh targets used by the dashboard HTML
|
|
21
|
+
if (path === '/api/metrics' || path === '/healthz') {
|
|
22
|
+
return proxy(DASHBOARD_ORIGIN + path + url.search, request);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const hasUpstash =
|
|
26
|
+
typeof UPSTASH_REDIS_REST_URL !== 'undefined' && !!UPSTASH_REDIS_REST_URL;
|
|
27
|
+
const body = {
|
|
28
|
+
service: 'utrecht-dataos-control-plane-api',
|
|
29
|
+
status: 'ok',
|
|
30
|
+
hostname: url.hostname,
|
|
31
|
+
path,
|
|
32
|
+
upstash_bound: hasUpstash,
|
|
33
|
+
dashboard: 'https://api.chefgroep.online/dashboard',
|
|
34
|
+
dashboard_origin: DASHBOARD_ORIGIN,
|
|
35
|
+
note: 'placeholder API surface for Utrecht Data OS control plane',
|
|
36
|
+
ts: new Date().toISOString(),
|
|
37
|
+
};
|
|
38
|
+
return new Response(JSON.stringify(body, null, 2), {
|
|
39
|
+
status: 200,
|
|
40
|
+
headers: { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' },
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function proxy(target, request) {
|
|
45
|
+
const upstream = await fetch(target, {
|
|
46
|
+
method: 'GET',
|
|
47
|
+
headers: { accept: request.headers.get('accept') || '*/*' },
|
|
48
|
+
redirect: 'follow',
|
|
49
|
+
});
|
|
50
|
+
const ct = upstream.headers.get('content-type') || 'text/html; charset=utf-8';
|
|
51
|
+
const headers = new Headers({ 'content-type': ct, 'cache-control': 'no-store' });
|
|
52
|
+
return new Response(upstream.body, { status: upstream.status, headers });
|
|
53
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Fleet deployment — agent memory
|
|
2
|
+
|
|
3
|
+
Connect every code-agent CLI on the fleet (Cursor, Codex, Claude, OpenCode, Grok)
|
|
4
|
+
to the shared Postgres memory layer.
|
|
5
|
+
|
|
6
|
+
## Topology
|
|
7
|
+
|
|
8
|
+
| Node | Shape | Role |
|
|
9
|
+
|------|-------|------|
|
|
10
|
+
| `bc-scan-arm` | A1.Flex (ARM, 23GB) | **Memory SSOT** — Postgres + vector (qdrant/weaviate) + llama.cpp |
|
|
11
|
+
| `bc-scan-2` | A1.Flex (ARM) | Inference / inventory worker |
|
|
12
|
+
| `bc-scan-1` | E2.1.Micro (x86) | Scanner |
|
|
13
|
+
| `bc-monitor` | E2.1.Micro (x86) | Monitoring |
|
|
14
|
+
| `sofie` (this host) | local | Dev + orchestration |
|
|
15
|
+
|
|
16
|
+
Postgres SSOT: `postgresql://agentmem:***@100.68.121.19:5432/agent_memory` (Tailscale).
|
|
17
|
+
|
|
18
|
+
## 1. Sync env to all nodes
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
cd python/deploy/fleet
|
|
22
|
+
./sync-env.sh # all nodes
|
|
23
|
+
./sync-env.sh bc-scan-arm # single node
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
This ships only the memory-related keys to `~/.agent-memory.env` on each node and
|
|
27
|
+
sources it from `~/.bashrc`.
|
|
28
|
+
|
|
29
|
+
## 2. Install the package on a node
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
./install-agent-memory.sh bc-scan-2
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 3. Wire code-agent CLIs
|
|
36
|
+
|
|
37
|
+
Each agent CLI inherits the DSN + keys because its shell sources `~/.agent-memory.env`.
|
|
38
|
+
Agents call memory three ways:
|
|
39
|
+
|
|
40
|
+
| Surface | How |
|
|
41
|
+
|---------|-----|
|
|
42
|
+
| Python | `from cursor_dreaming_memory import AgentMemory` |
|
|
43
|
+
| CLI | `dream-memory remember/recall/linear-ingest/notion-ingest` |
|
|
44
|
+
| Skill | `skills-bundle/shared/agent-memory.md` (drop into any agent skills dir) |
|
|
45
|
+
|
|
46
|
+
`session_type` is auto-detected per agent (cursor / codex / claude / opencode / grok)
|
|
47
|
+
via `SessionContext.from_sdk_payload()`.
|
|
48
|
+
|
|
49
|
+
## 4. Verify
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
ssh bc-scan-arm 'cd ~/Orgchefgroep/cursor-dreaming-sdk/python && uv run dream-memory recall --limit 5'
|
|
53
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
[Unit]
|
|
2
|
+
Description=Agent Memory metrics dashboard (always-on)
|
|
3
|
+
After=network-online.target
|
|
4
|
+
Wants=network-online.target
|
|
5
|
+
|
|
6
|
+
[Service]
|
|
7
|
+
Type=simple
|
|
8
|
+
WorkingDirectory=%h/cursor-dreaming-memory
|
|
9
|
+
ExecStart=/bin/bash -lc 'set -a; [ -f %h/.agent-memory.env ] && . %h/.agent-memory.env; set +a; exec %h/.local/bin/uv run dream-memory serve --host 0.0.0.0 --port 8787'
|
|
10
|
+
Restart=on-failure
|
|
11
|
+
RestartSec=5
|
|
12
|
+
|
|
13
|
+
[Install]
|
|
14
|
+
WantedBy=default.target
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
[Unit]
|
|
2
|
+
Description=Dream eval + Linear memory sync (oneshot)
|
|
3
|
+
After=network-online.target
|
|
4
|
+
Wants=network-online.target
|
|
5
|
+
|
|
6
|
+
[Service]
|
|
7
|
+
Type=oneshot
|
|
8
|
+
WorkingDirectory=%h/cursor-dreaming-memory
|
|
9
|
+
# Source the shell-style env file (export KEY="val") then run the sync flow.
|
|
10
|
+
ExecStart=/bin/bash -lc 'set -a; [ -f %h/.agent-memory.env ] && . %h/.agent-memory.env; set +a; exec %h/.local/bin/uv run python deploy/oci/prefect_flow.py'
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
[Unit]
|
|
2
|
+
Description=Schedule dream eval + Linear memory sync
|
|
3
|
+
|
|
4
|
+
[Timer]
|
|
5
|
+
# Daily 02:00 + Monday 09:00 (mirrors dream_eval_weekly automation)
|
|
6
|
+
OnCalendar=*-*-* 02:00:00
|
|
7
|
+
OnCalendar=Mon *-*-* 09:00:00
|
|
8
|
+
Persistent=true
|
|
9
|
+
|
|
10
|
+
[Install]
|
|
11
|
+
WantedBy=timers.target
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Create/Update a Cloudflare Tunnel that exposes the metrics dashboard on a domain.
|
|
3
|
+
|
|
4
|
+
Requires a Cloudflare API token with:
|
|
5
|
+
- Account → Cloudflare Tunnel: Edit
|
|
6
|
+
- Zone → DNS: Edit (on the target zone)
|
|
7
|
+
- Zone → Zone: Read
|
|
8
|
+
|
|
9
|
+
Env:
|
|
10
|
+
CLOUDFLARE_API_TOKEN (scoped as above)
|
|
11
|
+
CLOUDFLARE_ACCOUNT_ID
|
|
12
|
+
TUNNEL_HOSTNAME e.g. memory.chefgroep.nl
|
|
13
|
+
TUNNEL_NAME default: agent-memory
|
|
14
|
+
LOCAL_SERVICE default: http://localhost:8787
|
|
15
|
+
|
|
16
|
+
Outputs the tunnel run token. Install the connector with:
|
|
17
|
+
sudo cloudflared service install <TOKEN>
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import json
|
|
23
|
+
import os
|
|
24
|
+
import sys
|
|
25
|
+
import urllib.request
|
|
26
|
+
|
|
27
|
+
API = "https://api.cloudflare.com/client/v4"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _auth_headers() -> dict:
|
|
31
|
+
"""Bearer token, or legacy global-key (X-Auth-Email/Key) if provided."""
|
|
32
|
+
email = os.environ.get("CLOUDFLARE_EMAIL")
|
|
33
|
+
gkey = os.environ.get("CLOUDFLARE_GLOBAL_API_KEY")
|
|
34
|
+
if email and gkey:
|
|
35
|
+
return {"X-Auth-Email": email, "X-Auth-Key": gkey}
|
|
36
|
+
return {"Authorization": f"Bearer {os.environ['CLOUDFLARE_API_TOKEN']}"}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _req(method: str, path: str, token: str, body: dict | None = None) -> dict:
|
|
40
|
+
data = json.dumps(body).encode() if body is not None else None
|
|
41
|
+
headers = {"Content-Type": "application/json", **_auth_headers()}
|
|
42
|
+
req = urllib.request.Request(
|
|
43
|
+
f"{API}{path}",
|
|
44
|
+
data=data,
|
|
45
|
+
method=method,
|
|
46
|
+
headers=headers,
|
|
47
|
+
)
|
|
48
|
+
with urllib.request.urlopen(req, timeout=30) as resp:
|
|
49
|
+
out = json.loads(resp.read().decode())
|
|
50
|
+
if not out.get("success"):
|
|
51
|
+
raise SystemExit(f"CF API error {method} {path}: {out.get('errors')}")
|
|
52
|
+
return out
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def main() -> None:
|
|
56
|
+
token = os.environ.get("CLOUDFLARE_API_TOKEN", "")
|
|
57
|
+
acct = os.environ["CLOUDFLARE_ACCOUNT_ID"]
|
|
58
|
+
hostname = os.environ["TUNNEL_HOSTNAME"]
|
|
59
|
+
name = os.environ.get("TUNNEL_NAME", "agent-memory")
|
|
60
|
+
service = os.environ.get("LOCAL_SERVICE", "http://localhost:8787")
|
|
61
|
+
zone_name = ".".join(hostname.split(".")[-2:])
|
|
62
|
+
|
|
63
|
+
# 1. Resolve zone
|
|
64
|
+
zones = _req("GET", f"/zones?name={zone_name}", token)["result"]
|
|
65
|
+
if not zones:
|
|
66
|
+
raise SystemExit(f"Zone {zone_name} not found / not authorized")
|
|
67
|
+
zone_id = zones[0]["id"]
|
|
68
|
+
|
|
69
|
+
# 2. Create (or reuse) the tunnel — cloudflare-managed config
|
|
70
|
+
existing = _req("GET", f"/accounts/{acct}/cfd_tunnel?name={name}&is_deleted=false", token)
|
|
71
|
+
if existing["result"]:
|
|
72
|
+
tunnel = existing["result"][0]
|
|
73
|
+
tunnel_id = tunnel["id"]
|
|
74
|
+
tok = _req("GET", f"/accounts/{acct}/cfd_tunnel/{tunnel_id}/token", token)["result"]
|
|
75
|
+
else:
|
|
76
|
+
created = _req(
|
|
77
|
+
"POST",
|
|
78
|
+
f"/accounts/{acct}/cfd_tunnel",
|
|
79
|
+
token,
|
|
80
|
+
{"name": name, "config_src": "cloudflare"},
|
|
81
|
+
)["result"]
|
|
82
|
+
tunnel_id = created["id"]
|
|
83
|
+
tok = created["token"]
|
|
84
|
+
|
|
85
|
+
# 3. Configure ingress
|
|
86
|
+
_req(
|
|
87
|
+
"PUT",
|
|
88
|
+
f"/accounts/{acct}/cfd_tunnel/{tunnel_id}/configurations",
|
|
89
|
+
token,
|
|
90
|
+
{
|
|
91
|
+
"config": {
|
|
92
|
+
"ingress": [
|
|
93
|
+
{"hostname": hostname, "service": service},
|
|
94
|
+
{"service": "http_status:404"},
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# 4. DNS CNAME → tunnel
|
|
101
|
+
cname = f"{tunnel_id}.cfargotunnel.com"
|
|
102
|
+
recs = _req("GET", f"/zones/{zone_id}/dns_records?name={hostname}", token)["result"]
|
|
103
|
+
payload = {"type": "CNAME", "name": hostname, "content": cname, "proxied": True}
|
|
104
|
+
if recs:
|
|
105
|
+
_req("PUT", f"/zones/{zone_id}/dns_records/{recs[0]['id']}", token, payload)
|
|
106
|
+
else:
|
|
107
|
+
_req("POST", f"/zones/{zone_id}/dns_records", token, payload)
|
|
108
|
+
|
|
109
|
+
print(json.dumps({"tunnel_id": tunnel_id, "hostname": hostname, "url": f"https://{hostname}"}))
|
|
110
|
+
print("TUNNEL_TOKEN=" + tok, file=sys.stderr)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
if __name__ == "__main__":
|
|
114
|
+
main()
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Install the cursor-dreaming-memory package on a fleet node and verify the DB link.
|
|
3
|
+
# Usage: ./install-agent-memory.sh <node>
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
NODE="${1:?usage: install-agent-memory.sh <node>}"
|
|
6
|
+
REPO_DIR="/home/ubuntu/Orgchefgroep/cursor-dreaming-sdk"
|
|
7
|
+
|
|
8
|
+
ssh "$NODE" bash -s <<'REMOTE'
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
[ -f ~/.agent-memory.env ] && . ~/.agent-memory.env
|
|
11
|
+
command -v uv >/dev/null 2>&1 || curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
12
|
+
mkdir -p ~/Orgchefgroep
|
|
13
|
+
if [ ! -d ~/Orgchefgroep/cursor-dreaming-sdk ]; then
|
|
14
|
+
gh repo clone OnlineChefGroep/cursor-dreaming-sdk ~/Orgchefgroep/cursor-dreaming-sdk 2>/dev/null \
|
|
15
|
+
|| git clone https://github.com/OnlineChefGroep/cursor-dreaming-sdk ~/Orgchefgroep/cursor-dreaming-sdk
|
|
16
|
+
fi
|
|
17
|
+
cd ~/Orgchefgroep/cursor-dreaming-sdk/python
|
|
18
|
+
git pull --ff-only 2>/dev/null || true
|
|
19
|
+
uv sync
|
|
20
|
+
echo "--- verifying DB link ---"
|
|
21
|
+
uv run python -c "from cursor_dreaming_memory import AgentMemory; m=AgentMemory(enable_sentry=False); print(m.config.redacted()); m.ensure_schema(); print('schema OK')"
|
|
22
|
+
REMOTE
|
|
23
|
+
echo "Installed agent-memory on $NODE"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Idempotent installer: wire the agent memory layer into one fleet host.
|
|
3
|
+
# Connects every code-agent CLI on the host to the shared OCI Postgres SSOT.
|
|
4
|
+
#
|
|
5
|
+
# Usage (from sofie control node):
|
|
6
|
+
# for h in bc-monitor bc-scan-arm bc-scan-2; do
|
|
7
|
+
# scp -r python "$h:~/cursor-dreaming-memory"
|
|
8
|
+
# ssh "$h" 'bash ~/cursor-dreaming-memory/deploy/fleet/install-memory.sh'
|
|
9
|
+
# done
|
|
10
|
+
#
|
|
11
|
+
# Secrets are read at runtime from ~/.config/agent-memory/.env on each host.
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
|
|
14
|
+
REPO_DIR="${REPO_DIR:-$HOME/cursor-dreaming-memory}"
|
|
15
|
+
ENV_DIR="$HOME/.config/agent-memory"
|
|
16
|
+
ENV_FILE="$ENV_DIR/.env"
|
|
17
|
+
|
|
18
|
+
echo "[*] Installing agent memory on $(hostname)"
|
|
19
|
+
|
|
20
|
+
# 1. uv
|
|
21
|
+
if ! command -v uv >/dev/null 2>&1; then
|
|
22
|
+
echo "[*] Installing uv"
|
|
23
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
24
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# 2. Python deps
|
|
28
|
+
cd "$REPO_DIR"
|
|
29
|
+
uv sync --extra all
|
|
30
|
+
|
|
31
|
+
# 3. Secrets file (filled out-of-band; never committed)
|
|
32
|
+
mkdir -p "$ENV_DIR"
|
|
33
|
+
if [ ! -f "$ENV_FILE" ]; then
|
|
34
|
+
cat > "$ENV_FILE" <<'EOF'
|
|
35
|
+
# Per-host agent memory secrets (chmod 600). Fill these in.
|
|
36
|
+
AGENT_MEMORY_DATABASE_URL=
|
|
37
|
+
LINEAR_API_KEY=
|
|
38
|
+
NOTION_API_KEY=
|
|
39
|
+
CLOUDFLARE_API_TOKEN=
|
|
40
|
+
CLOUDFLARE_ACCOUNT_ID=
|
|
41
|
+
SENTRY_DSN=
|
|
42
|
+
SENTRY_ENVIRONMENT=fleet
|
|
43
|
+
EOF
|
|
44
|
+
chmod 600 "$ENV_FILE"
|
|
45
|
+
echo "[!] Created $ENV_FILE — fill in secrets, then re-run doctor."
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# 4. Verify
|
|
49
|
+
set +e
|
|
50
|
+
FLEET_ENV_FILE="$ENV_FILE" uv run dream-memory doctor
|
|
51
|
+
echo "[*] Done. Agents on this host can now: uv run --project $REPO_DIR dream-memory ..."
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Sync agent-memory env (DSN + secrets) to all fleet nodes over Tailscale.
|
|
3
|
+
# Usage: ./sync-env.sh [node ...] (defaults to all known nodes)
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
NODES=("${@:-bc-scan-arm bc-scan-2 bc-scan-1 bc-monitor}")
|
|
7
|
+
SRC="${HOME}/.openclaude/.env"
|
|
8
|
+
REMOTE_ENV="/home/ubuntu/.agent-memory.env"
|
|
9
|
+
|
|
10
|
+
if [[ ! -f "$SRC" ]]; then
|
|
11
|
+
echo "ERROR: $SRC not found" >&2
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
# Only ship the keys the memory layer needs (never the full secrets file).
|
|
16
|
+
KEYS=(AGENT_MEMORY_DATABASE_URL LINEAR_API_KEY LINEAR_TEAM_KEY NOTION_API_KEY NOTION_TOKEN \
|
|
17
|
+
CLOUDFLARE_API_TOKEN CLOUDFLARE_ACCOUNT_ID R2_BUCKET R2_ACCESS_KEY_ID R2_SECRET_ACCESS_KEY R2_ENDPOINT \
|
|
18
|
+
SENTRY_DSN SENTRY_ENVIRONMENT)
|
|
19
|
+
|
|
20
|
+
TMP="$(mktemp)"
|
|
21
|
+
# shellcheck disable=SC1090
|
|
22
|
+
source "$SRC"
|
|
23
|
+
for k in "${KEYS[@]}"; do
|
|
24
|
+
v="${!k:-}"
|
|
25
|
+
[[ -n "$v" ]] && echo "export $k=\"$v\"" >> "$TMP"
|
|
26
|
+
done
|
|
27
|
+
|
|
28
|
+
for node in ${NODES[@]}; do
|
|
29
|
+
echo "→ $node"
|
|
30
|
+
scp -q "$TMP" "$node:$REMOTE_ENV"
|
|
31
|
+
# Ensure interactive + non-interactive shells load it
|
|
32
|
+
ssh "$node" "grep -q 'agent-memory.env' ~/.bashrc 2>/dev/null || echo '[ -f ~/.agent-memory.env ] && . ~/.agent-memory.env' >> ~/.bashrc"
|
|
33
|
+
done
|
|
34
|
+
rm -f "$TMP"
|
|
35
|
+
echo "Done. Memory env synced to: ${NODES[*]}"
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# OCI deployment — agent memory on Ampere A1
|
|
2
|
+
|
|
3
|
+
Deploy the cursor-dreaming-memory layer on Oracle Cloud Free Tier (ARM).
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- OCI CLI configured (`~/.oci/config`)
|
|
8
|
+
- Ampere A1 compute instance (Ubuntu 22.04+ ARM64)
|
|
9
|
+
- SSH access to the instance
|
|
10
|
+
|
|
11
|
+
## 1. Provision instance (optional)
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# List available shapes in your compartment
|
|
15
|
+
oci compute shape list --compartment-id <COMPARTMENT_OCID>
|
|
16
|
+
|
|
17
|
+
# Create instance (example — adjust image OCID for your region)
|
|
18
|
+
oci compute instance launch \
|
|
19
|
+
--availability-domain <AD> \
|
|
20
|
+
--compartment-id <COMPARTMENT_OCID> \
|
|
21
|
+
--shape VM.Standard.A1.Flex \
|
|
22
|
+
--shape-config '{"ocpus":1,"memoryInGBs":6}' \
|
|
23
|
+
--display-name agent-memory-a1 \
|
|
24
|
+
--image-id <UBUNTU_ARM_IMAGE_OCID> \
|
|
25
|
+
--subnet-id <SUBNET_OCID> \
|
|
26
|
+
--assign-public-ip true
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 2. Copy code to instance
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
rsync -avz python/ ubuntu@<INSTANCE_IP>:~/cursor-dreaming-memory/
|
|
33
|
+
scp .env ubuntu@<INSTANCE_IP>:~/cursor-dreaming-memory/.env
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 3. Start Postgres
|
|
37
|
+
|
|
38
|
+
On the instance:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
cd ~/cursor-dreaming-memory/deploy/oci
|
|
42
|
+
docker compose up -d
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Postgres listens on `localhost:5432`, database `agent_memory`.
|
|
46
|
+
|
|
47
|
+
## 4. Install Python package
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
cd ~/cursor-dreaming-memory
|
|
51
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
52
|
+
uv sync
|
|
53
|
+
export AGENT_MEMORY_DATABASE_URL=postgresql://postgres:postgres@localhost:5432/agent_memory
|
|
54
|
+
uv run dream-memory init
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## 5. Environment variables
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
export LINEAR_API_KEY=lin_api_...
|
|
61
|
+
export NOTION_API_KEY=secret_...
|
|
62
|
+
export AGENT_MEMORY_DATABASE_URL=postgresql://postgres:postgres@localhost:5432/agent_memory
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## 6. Optional: Prefect scheduled sync
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
uv sync --extra prefect
|
|
69
|
+
uv run python deploy/oci/prefect_flow.py
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Runs a daily Linear issue ingest for configured CHEF/GROEP tickets.
|
|
73
|
+
|
|
74
|
+
## 7. systemd service (optional)
|
|
75
|
+
|
|
76
|
+
```ini
|
|
77
|
+
# /etc/systemd/system/agent-memory.service
|
|
78
|
+
[Unit]
|
|
79
|
+
Description=Agent Memory Prefect Worker
|
|
80
|
+
After=docker.service
|
|
81
|
+
|
|
82
|
+
[Service]
|
|
83
|
+
Type=simple
|
|
84
|
+
User=ubuntu
|
|
85
|
+
WorkingDirectory=/home/ubuntu/cursor-dreaming-memory
|
|
86
|
+
EnvironmentFile=/home/ubuntu/cursor-dreaming-memory/.env
|
|
87
|
+
ExecStart=/home/ubuntu/.local/bin/uv run python deploy/oci/prefect_flow.py
|
|
88
|
+
Restart=on-failure
|
|
89
|
+
|
|
90
|
+
[Install]
|
|
91
|
+
WantedBy=multi-user.target
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## ARM notes
|
|
95
|
+
|
|
96
|
+
- Use `psycopg[binary]` wheels (included) — no x86-only deps
|
|
97
|
+
- LanceDB optional extra works on aarch64 when installed via pip
|
|
98
|
+
- Keep memory lightweight: 1 OCPU / 6 GB RAM is sufficient for dev
|