@pleri/olam-cli 0.1.206 → 0.1.207
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/dist/image-digests.json +8 -8
- package/dist/index.js +1847 -1384
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.js +1066 -789
- package/hermes-bundle/version.json +1 -1
- package/host-cp/k8s/manifests/50-deployment.yaml +1 -1
- package/host-cp/k8s/manifests/auth-service/50-deployment.yaml +1 -1
- package/host-cp/k8s/manifests/kg-service/50-deployment.yaml +1 -1
- package/host-cp/k8s/manifests/mcp-auth-service/50-deployment.yaml +1 -1
- package/host-cp/k8s/manifests/memory-service/50-deployment.yaml +1 -1
- package/memory-hooks/agentmemory-save.mjs +138 -0
- package/package.json +1 -1
|
@@ -118,7 +118,7 @@ spec:
|
|
|
118
118
|
# k3d), started by `olam upgrade` Step 0.7 — not inside this Pod.
|
|
119
119
|
containers:
|
|
120
120
|
- name: olam-host-cp
|
|
121
|
-
image: ghcr.io/pleri/olam-host-cp@sha256:
|
|
121
|
+
image: ghcr.io/pleri/olam-host-cp@sha256:0d1869ed3c823e6ea1b5d885d2f73d7418134c1b82c9dd70719044fd04d74027
|
|
122
122
|
imagePullPolicy: IfNotPresent
|
|
123
123
|
securityContext:
|
|
124
124
|
runAsNonRoot: true
|
|
@@ -70,7 +70,7 @@ spec:
|
|
|
70
70
|
mountPath: /data
|
|
71
71
|
containers:
|
|
72
72
|
- name: olam-auth-service
|
|
73
|
-
image: ghcr.io/pleri/olam-auth@sha256:
|
|
73
|
+
image: ghcr.io/pleri/olam-auth@sha256:9f8195a20727ec844d7393696c298b42e3e83e841fe684787ee9c6d1e301b5e8
|
|
74
74
|
imagePullPolicy: IfNotPresent
|
|
75
75
|
securityContext:
|
|
76
76
|
runAsNonRoot: true
|
|
@@ -61,7 +61,7 @@ spec:
|
|
|
61
61
|
mountPath: /data
|
|
62
62
|
containers:
|
|
63
63
|
- name: olam-kg-service
|
|
64
|
-
image: ghcr.io/pleri/olam-kg-service@sha256:
|
|
64
|
+
image: ghcr.io/pleri/olam-kg-service@sha256:0b1f78a566675509f371e4e0636a76859f800c1bec787f06742e3a4f116d7c90
|
|
65
65
|
imagePullPolicy: IfNotPresent
|
|
66
66
|
securityContext:
|
|
67
67
|
runAsNonRoot: true
|
|
@@ -68,7 +68,7 @@ spec:
|
|
|
68
68
|
mountPath: /data
|
|
69
69
|
containers:
|
|
70
70
|
- name: olam-mcp-auth-service
|
|
71
|
-
image: ghcr.io/pleri/olam-mcp-auth@sha256:
|
|
71
|
+
image: ghcr.io/pleri/olam-mcp-auth@sha256:63ecc6bdd5c3def600a78046f0e7ab36938e88bc6fc7d31a446d7cec84a27a3a
|
|
72
72
|
imagePullPolicy: IfNotPresent
|
|
73
73
|
securityContext:
|
|
74
74
|
runAsNonRoot: true
|
|
@@ -70,7 +70,7 @@ spec:
|
|
|
70
70
|
# bootstrap-placeholder comment + run `npm run refresh:manifest-digests`
|
|
71
71
|
# once ghcr.io/pleri/olam-memory-service has a real published digest.
|
|
72
72
|
# bootstrap-placeholder: pre-publish; refresh after first release
|
|
73
|
-
image: ghcr.io/pleri/olam-memory-service@sha256:
|
|
73
|
+
image: ghcr.io/pleri/olam-memory-service@sha256:673156cc638fc9af096e73fc7cd7c666373f92e63e566a57f51271b691aa9d74
|
|
74
74
|
imagePullPolicy: IfNotPresent
|
|
75
75
|
securityContext:
|
|
76
76
|
runAsNonRoot: true
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* agentmemory-save.mjs — PostToolUse hook: persist agent-written memory notes.
|
|
4
|
+
*
|
|
5
|
+
* AM_SENTINEL=olam-agent-memory-hook-v2
|
|
6
|
+
*
|
|
7
|
+
* This is the FILE-BASED port of the legacy inline python SAVE blob that used
|
|
8
|
+
* to live directly in ~/.claude/settings.json under
|
|
9
|
+
* `AM_SENTINEL=olam-agent-memory-hook-v1; python3 -c '…'`. The installer
|
|
10
|
+
* (`olam hooks install-all`, invoked by `olam skills sync`) copies this file to
|
|
11
|
+
* ~/.claude/scripts/hooks/ and registers a settings.json entry pointing at it,
|
|
12
|
+
* REPLACING the inline blob. Keeping the logic in a real source file means it
|
|
13
|
+
* is version-controlled, testable, bundled into @pleri/olam-cli, and owned by
|
|
14
|
+
* sync — not stranded as a string in the operator's settings.
|
|
15
|
+
*
|
|
16
|
+
* Behaviour (byte-faithful port of the v1 blob):
|
|
17
|
+
* - Reads the PostToolUse JSON payload from stdin.
|
|
18
|
+
* - Acts ONLY when tool_name === "Write" AND the written file_path matches
|
|
19
|
+
* /\/\.claude\/projects\/.*\/memory\/.*\.md$/ ; otherwise bails (exit 0).
|
|
20
|
+
* - Requires OLAM_AGENT_MEMORY_BEARER (trimmed, non-empty); else bails.
|
|
21
|
+
* - Parses YAML-ish frontmatter delimited by leading "---":
|
|
22
|
+
* description: → title (default "memory") ← keyed on description, per v1
|
|
23
|
+
* type: → type (default "fact")
|
|
24
|
+
* remainder → body
|
|
25
|
+
* - POSTs {title, content: body, type} to
|
|
26
|
+
* <OLAM_AGENT_MEMORY_URL|default>/agentmemory/remember
|
|
27
|
+
* with Authorization: Bearer <bearer>, ~3s timeout.
|
|
28
|
+
* - On success prints to STDERR the green status line:
|
|
29
|
+
* [🧠⇡ Memory saved] "<title first 60 chars>"
|
|
30
|
+
* - Fail-open: ANY error → silent, exit 0. Never throws to the caller.
|
|
31
|
+
*
|
|
32
|
+
* Node built-ins only (global fetch + AbortController, available on Node ≥18).
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import { readFileSync } from 'node:fs';
|
|
36
|
+
|
|
37
|
+
const DEFAULT_URL = 'https://atlas-agent-memory.atlas-kitchen.workers.dev';
|
|
38
|
+
const MEMORY_PATH_RE = /\/\.claude\/projects\/.*\/memory\/.*\.md$/;
|
|
39
|
+
const TIMEOUT_MS = 3000;
|
|
40
|
+
|
|
41
|
+
/** Read all of stdin as a UTF-8 string. */
|
|
42
|
+
function readStdin() {
|
|
43
|
+
try {
|
|
44
|
+
return readFileSync(0, 'utf8');
|
|
45
|
+
} catch {
|
|
46
|
+
return '';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Parse leading "---"-delimited frontmatter. Returns {title, type, body}.
|
|
52
|
+
* Mirrors the v1 blob: title comes from `description:`, type from `type:`.
|
|
53
|
+
*/
|
|
54
|
+
function parseNote(raw) {
|
|
55
|
+
let title = 'memory';
|
|
56
|
+
let type = 'fact';
|
|
57
|
+
let body = raw;
|
|
58
|
+
if (raw.startsWith('---')) {
|
|
59
|
+
// Split into [before-first-delim, frontmatter, …rest]. JS's limited split
|
|
60
|
+
// truncates the remainder, so slice manually to keep the full body.
|
|
61
|
+
const firstDelimEnd = 3; // length of leading "---"
|
|
62
|
+
const secondDelim = raw.indexOf('---', firstDelimEnd);
|
|
63
|
+
if (secondDelim !== -1) {
|
|
64
|
+
const fm = raw.slice(firstDelimEnd, secondDelim);
|
|
65
|
+
body = raw.slice(secondDelim + 3).trim();
|
|
66
|
+
for (const ln of fm.split('\n')) {
|
|
67
|
+
const s = ln.trim();
|
|
68
|
+
if (s.startsWith('description:')) {
|
|
69
|
+
title = stripQuotes(s.slice(s.indexOf(':') + 1).trim());
|
|
70
|
+
} else if (s.startsWith('type:')) {
|
|
71
|
+
type = stripQuotes(s.slice(s.indexOf(':') + 1).trim());
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return { title, type, body };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function stripQuotes(v) {
|
|
80
|
+
return v.replace(/^["']/, '').replace(/["']$/, '');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function main() {
|
|
84
|
+
const payloadRaw = readStdin();
|
|
85
|
+
let payload;
|
|
86
|
+
try {
|
|
87
|
+
payload = JSON.parse(payloadRaw);
|
|
88
|
+
} catch {
|
|
89
|
+
return; // not parseable → fail-open
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const toolName = payload?.tool_name ?? '';
|
|
93
|
+
const filePath = payload?.tool_input?.file_path ?? '';
|
|
94
|
+
if (toolName !== 'Write' || !MEMORY_PATH_RE.test(filePath)) return;
|
|
95
|
+
|
|
96
|
+
const bearer = (process.env['OLAM_AGENT_MEMORY_BEARER'] ?? '').trim();
|
|
97
|
+
if (!bearer) return;
|
|
98
|
+
|
|
99
|
+
let raw;
|
|
100
|
+
try {
|
|
101
|
+
raw = readFileSync(filePath, 'utf8');
|
|
102
|
+
} catch {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const { title, type, body } = parseNote(raw);
|
|
107
|
+
const base = (process.env['OLAM_AGENT_MEMORY_URL'] ?? DEFAULT_URL).replace(/\/$/, '');
|
|
108
|
+
const url = `${base}/agentmemory/remember`;
|
|
109
|
+
|
|
110
|
+
const controller = new AbortController();
|
|
111
|
+
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
112
|
+
try {
|
|
113
|
+
const resp = await fetch(url, {
|
|
114
|
+
method: 'POST',
|
|
115
|
+
headers: {
|
|
116
|
+
'Content-Type': 'application/json',
|
|
117
|
+
Authorization: `Bearer ${bearer}`,
|
|
118
|
+
'User-Agent': 'olam-agent-memory-hook/2',
|
|
119
|
+
},
|
|
120
|
+
body: JSON.stringify({ title, content: body, type }),
|
|
121
|
+
signal: controller.signal,
|
|
122
|
+
});
|
|
123
|
+
// Treat any non-throwing response as delivered (the v1 blob did the same —
|
|
124
|
+
// it printed on a non-exception urlopen regardless of status code).
|
|
125
|
+
if (resp) {
|
|
126
|
+
const label = title.slice(0, 60);
|
|
127
|
+
process.stderr.write(`\x1b[32m[\u{1F9E0}\u{21E1} Memory saved]\x1b[0m "${label}"\n`);
|
|
128
|
+
}
|
|
129
|
+
} catch {
|
|
130
|
+
// Fail-open: network error / timeout / abort → silent.
|
|
131
|
+
} finally {
|
|
132
|
+
clearTimeout(timer);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
main().catch(() => {
|
|
137
|
+
/* fail-open: never propagate to the caller */
|
|
138
|
+
});
|