openclaw-memory-atmem 1.0.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 +18 -0
- package/dist/index.js +1342 -0
- package/dist/src/rpc-client.js +190 -0
- package/dist/src/setup.js +120 -0
- package/dist/src/types.js +7 -0
- package/openclaw.plugin.json +120 -0
- package/package.json +47 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Newline-delimited JSON-RPC 2.0 client for the `atmem mcp` stdio server.
|
|
3
|
+
*
|
|
4
|
+
* The child process is spawned lazily on first call, the MCP initialize
|
|
5
|
+
* handshake runs once, and calls are matched to responses by request id.
|
|
6
|
+
* If the child dies it is respawned on the next call.
|
|
7
|
+
*/
|
|
8
|
+
import { spawn } from "node:child_process";
|
|
9
|
+
import { createInterface } from "node:readline";
|
|
10
|
+
export class AtmemClient {
|
|
11
|
+
options;
|
|
12
|
+
child = null;
|
|
13
|
+
reader = null;
|
|
14
|
+
pending = new Map();
|
|
15
|
+
nextId = 1;
|
|
16
|
+
initialized = null;
|
|
17
|
+
idleTimer = null;
|
|
18
|
+
toolNames = null;
|
|
19
|
+
constructor(options) {
|
|
20
|
+
this.options = options;
|
|
21
|
+
}
|
|
22
|
+
/** Verify that the engine starts and completes the MCP handshake. */
|
|
23
|
+
async connect() {
|
|
24
|
+
try {
|
|
25
|
+
this.cancelIdleClose();
|
|
26
|
+
await this.ensureInitialized();
|
|
27
|
+
}
|
|
28
|
+
finally {
|
|
29
|
+
this.scheduleIdleClose();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** Call an MCP tool; returns the parsed JSON payload of the text block. */
|
|
33
|
+
async callTool(name, args, timeoutMs) {
|
|
34
|
+
try {
|
|
35
|
+
this.cancelIdleClose();
|
|
36
|
+
await this.ensureInitialized();
|
|
37
|
+
const result = (await this.request("tools/call", { name, arguments: args }, timeoutMs));
|
|
38
|
+
const text = result?.content?.[0]?.text ?? "";
|
|
39
|
+
if (result?.isError) {
|
|
40
|
+
throw new Error(`atmem tool ${name} failed: ${text}`);
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
return JSON.parse(text);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return text;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
this.scheduleIdleClose();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** Discover optional server capabilities without assuming a package version. */
|
|
54
|
+
async hasTool(name, timeoutMs) {
|
|
55
|
+
try {
|
|
56
|
+
this.cancelIdleClose();
|
|
57
|
+
if (this.toolNames === null) {
|
|
58
|
+
await this.ensureInitialized();
|
|
59
|
+
const result = (await this.request("tools/list", {}, timeoutMs));
|
|
60
|
+
this.toolNames = new Set((result?.tools ?? [])
|
|
61
|
+
.map((tool) => String(tool.name ?? ""))
|
|
62
|
+
.filter(Boolean));
|
|
63
|
+
}
|
|
64
|
+
return this.toolNames.has(name);
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
this.scheduleIdleClose();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
close() {
|
|
71
|
+
this.cancelIdleClose();
|
|
72
|
+
if (this.child) {
|
|
73
|
+
this.child.stdin.end();
|
|
74
|
+
this.child.kill();
|
|
75
|
+
}
|
|
76
|
+
this.teardown(new Error("client closed"));
|
|
77
|
+
}
|
|
78
|
+
cancelIdleClose() {
|
|
79
|
+
if (this.idleTimer)
|
|
80
|
+
clearTimeout(this.idleTimer);
|
|
81
|
+
this.idleTimer = null;
|
|
82
|
+
}
|
|
83
|
+
scheduleIdleClose() {
|
|
84
|
+
this.cancelIdleClose();
|
|
85
|
+
const timeout = this.options.idleTimeoutMs ?? 250;
|
|
86
|
+
this.idleTimer = setTimeout(() => this.close(), timeout);
|
|
87
|
+
}
|
|
88
|
+
async ensureInitialized() {
|
|
89
|
+
if (!this.child || this.child.exitCode !== null) {
|
|
90
|
+
this.startChild();
|
|
91
|
+
this.initialized = this.handshake();
|
|
92
|
+
}
|
|
93
|
+
await this.initialized;
|
|
94
|
+
}
|
|
95
|
+
startChild() {
|
|
96
|
+
const { command, args } = this.options;
|
|
97
|
+
this.options.log?.(`[atmem] spawning: ${command} ${args.join(" ")}`);
|
|
98
|
+
const child = spawn(command, args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
99
|
+
this.child = child;
|
|
100
|
+
this.reader = createInterface({ input: child.stdout });
|
|
101
|
+
this.reader.on("line", (line) => this.onLine(line));
|
|
102
|
+
child.stderr.on("data", (chunk) => {
|
|
103
|
+
const text = chunk.toString().trim();
|
|
104
|
+
if (text)
|
|
105
|
+
this.options.logError?.(`[atmem stderr] ${text}`);
|
|
106
|
+
});
|
|
107
|
+
child.on("exit", (code) => {
|
|
108
|
+
this.options.logError?.(`[atmem] server exited (code ${code})`);
|
|
109
|
+
this.teardown(new Error(`atmem server exited (code ${code})`));
|
|
110
|
+
});
|
|
111
|
+
child.on("error", (error) => {
|
|
112
|
+
const spawnError = error;
|
|
113
|
+
const actionable = spawnError.code === "ENOENT"
|
|
114
|
+
? new Error(`AtMem engine executable '${command}' was not found. ` +
|
|
115
|
+
"Install the engine first with `python3 -m pip install atmem`, " +
|
|
116
|
+
"verify `atmem --version`, or set the plugin `command` option " +
|
|
117
|
+
"to the executable's absolute path.")
|
|
118
|
+
: error instanceof Error
|
|
119
|
+
? error
|
|
120
|
+
: new Error(String(error));
|
|
121
|
+
this.options.logError?.(`[atmem] spawn failed: ${actionable.message}`);
|
|
122
|
+
this.teardown(actionable);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
async handshake() {
|
|
126
|
+
await this.request("initialize", {
|
|
127
|
+
protocolVersion: "2025-06-18",
|
|
128
|
+
capabilities: {},
|
|
129
|
+
clientInfo: {
|
|
130
|
+
name: "openclaw-memory-atmem",
|
|
131
|
+
version: "1.0.0",
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
this.notify("notifications/initialized", {});
|
|
135
|
+
}
|
|
136
|
+
request(method, params, timeoutMs) {
|
|
137
|
+
const child = this.child;
|
|
138
|
+
if (!child)
|
|
139
|
+
return Promise.reject(new Error("atmem server not running"));
|
|
140
|
+
const id = this.nextId++;
|
|
141
|
+
const timeout = timeoutMs ?? this.options.defaultTimeoutMs ?? 10_000;
|
|
142
|
+
return new Promise((resolve, reject) => {
|
|
143
|
+
const timer = setTimeout(() => {
|
|
144
|
+
this.pending.delete(id);
|
|
145
|
+
reject(new Error(`atmem ${method} timed out after ${timeout}ms`));
|
|
146
|
+
}, timeout);
|
|
147
|
+
this.pending.set(id, { resolve, reject, timer });
|
|
148
|
+
child.stdin.write(JSON.stringify({ jsonrpc: "2.0", id, method, params }) + "\n");
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
notify(method, params) {
|
|
152
|
+
this.child?.stdin.write(JSON.stringify({ jsonrpc: "2.0", method, params }) + "\n");
|
|
153
|
+
}
|
|
154
|
+
onLine(line) {
|
|
155
|
+
if (!line.trim())
|
|
156
|
+
return;
|
|
157
|
+
let message;
|
|
158
|
+
try {
|
|
159
|
+
message = JSON.parse(line);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
this.options.logError?.(`[atmem] unparseable line: ${line.slice(0, 200)}`);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (message.id === undefined)
|
|
166
|
+
return;
|
|
167
|
+
const pending = this.pending.get(message.id);
|
|
168
|
+
if (!pending)
|
|
169
|
+
return;
|
|
170
|
+
this.pending.delete(message.id);
|
|
171
|
+
clearTimeout(pending.timer);
|
|
172
|
+
if (message.error) {
|
|
173
|
+
pending.reject(new Error(`atmem rpc error ${message.error.code}: ${message.error.message}`));
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
pending.resolve(message.result);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
teardown(error) {
|
|
180
|
+
for (const pending of this.pending.values()) {
|
|
181
|
+
clearTimeout(pending.timer);
|
|
182
|
+
pending.reject(error);
|
|
183
|
+
}
|
|
184
|
+
this.pending.clear();
|
|
185
|
+
this.reader?.close();
|
|
186
|
+
this.reader = null;
|
|
187
|
+
this.child = null;
|
|
188
|
+
this.initialized = null;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { accessSync, constants } from "node:fs";
|
|
2
|
+
import { delimiter, isAbsolute } from "node:path";
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
function executableExists(command) {
|
|
5
|
+
if (command.includes("/") || isAbsolute(command)) {
|
|
6
|
+
try {
|
|
7
|
+
accessSync(command, constants.X_OK);
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return (process.env.PATH ?? "")
|
|
15
|
+
.split(delimiter)
|
|
16
|
+
.some((entry) => {
|
|
17
|
+
try {
|
|
18
|
+
accessSync(`${entry}/${command}`, constants.X_OK);
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function currentOpenClawInvocation() {
|
|
27
|
+
const script = process.argv[1];
|
|
28
|
+
if (script && /node(?:\.exe)?$/i.test(process.execPath)) {
|
|
29
|
+
return { command: process.execPath, prefix: [script] };
|
|
30
|
+
}
|
|
31
|
+
return { command: process.argv[0] || "openclaw", prefix: [] };
|
|
32
|
+
}
|
|
33
|
+
export function setupWrites(options) {
|
|
34
|
+
const config = {
|
|
35
|
+
command: options.command,
|
|
36
|
+
dbPath: options.dbPath,
|
|
37
|
+
subject: options.subject,
|
|
38
|
+
recall: {
|
|
39
|
+
enabled: true,
|
|
40
|
+
maxRecords: 3,
|
|
41
|
+
maxChars: 1200,
|
|
42
|
+
minScore: 0.3,
|
|
43
|
+
timeoutMs: 3000,
|
|
44
|
+
},
|
|
45
|
+
persona: { enabled: true, maxChars: 600, ttlSeconds: 300 },
|
|
46
|
+
capture: { enabled: true, captureAssistant: true },
|
|
47
|
+
cacheAware: { enabled: true, compactReferences: true },
|
|
48
|
+
tools: { enabled: true },
|
|
49
|
+
};
|
|
50
|
+
return [
|
|
51
|
+
["plugins.entries.memory-atmem.enabled", "true", true],
|
|
52
|
+
["plugins.entries.memory-atmem.hooks.allowConversationAccess", "true", true],
|
|
53
|
+
[
|
|
54
|
+
"plugins.entries.memory-atmem.config.command",
|
|
55
|
+
JSON.stringify(config.command),
|
|
56
|
+
true,
|
|
57
|
+
],
|
|
58
|
+
[
|
|
59
|
+
"plugins.entries.memory-atmem.config.dbPath",
|
|
60
|
+
JSON.stringify(config.dbPath),
|
|
61
|
+
true,
|
|
62
|
+
],
|
|
63
|
+
[
|
|
64
|
+
"plugins.entries.memory-atmem.config.subject",
|
|
65
|
+
JSON.stringify(config.subject),
|
|
66
|
+
true,
|
|
67
|
+
],
|
|
68
|
+
[
|
|
69
|
+
"plugins.entries.memory-atmem.config.recall",
|
|
70
|
+
JSON.stringify(config.recall),
|
|
71
|
+
true,
|
|
72
|
+
],
|
|
73
|
+
[
|
|
74
|
+
"plugins.entries.memory-atmem.config.persona",
|
|
75
|
+
JSON.stringify(config.persona),
|
|
76
|
+
true,
|
|
77
|
+
],
|
|
78
|
+
[
|
|
79
|
+
"plugins.entries.memory-atmem.config.capture",
|
|
80
|
+
JSON.stringify(config.capture),
|
|
81
|
+
true,
|
|
82
|
+
],
|
|
83
|
+
[
|
|
84
|
+
"plugins.entries.memory-atmem.config.cacheAware",
|
|
85
|
+
JSON.stringify(config.cacheAware),
|
|
86
|
+
true,
|
|
87
|
+
],
|
|
88
|
+
[
|
|
89
|
+
"plugins.entries.memory-atmem.config.tools",
|
|
90
|
+
JSON.stringify(config.tools),
|
|
91
|
+
true,
|
|
92
|
+
],
|
|
93
|
+
];
|
|
94
|
+
}
|
|
95
|
+
export async function runSetup(options, dependencies = {}) {
|
|
96
|
+
if (!options.subject.trim())
|
|
97
|
+
throw new Error("--subject must not be empty");
|
|
98
|
+
if (!executableExists(options.command)) {
|
|
99
|
+
throw new Error(`AtMem executable not found: ${options.command}. Run: python3 -m pip install --upgrade atmem`);
|
|
100
|
+
}
|
|
101
|
+
const invocation = dependencies.invocation ?? currentOpenClawInvocation();
|
|
102
|
+
const runner = dependencies.runner ?? ((command, args) => spawnSync(command, args, { stdio: "inherit" }));
|
|
103
|
+
for (const [key, value, strictJson] of setupWrites(options)) {
|
|
104
|
+
const args = [...invocation.prefix, "config", "set", key, value];
|
|
105
|
+
if (strictJson)
|
|
106
|
+
args.push("--strict-json");
|
|
107
|
+
const result = runner(invocation.command, args);
|
|
108
|
+
if (result.status !== 0) {
|
|
109
|
+
throw new Error(`OpenClaw rejected configuration key: ${key}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (options.restart) {
|
|
113
|
+
const result = runner(invocation.command, [...invocation.prefix, "gateway", "restart"]);
|
|
114
|
+
if (result.status !== 0) {
|
|
115
|
+
throw new Error("Configuration was saved, but the OpenClaw gateway restart failed");
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
process.stdout.write("AtMem bounded recall and capture are configured. " +
|
|
119
|
+
"Use `atmem openclaw install` for verified shadowing and reversible activation.\n");
|
|
120
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal structural types for the OpenClaw plugin API surface this plugin
|
|
3
|
+
* uses. Kept local (instead of importing "openclaw/plugin-sdk/core") so the
|
|
4
|
+
* plugin builds without the SDK on the compile path; the shapes match the
|
|
5
|
+
* hook/tool contracts observed in OpenClaw's plugin SDK.
|
|
6
|
+
*/
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "memory-atmem",
|
|
3
|
+
"name": "Memory (atmem)",
|
|
4
|
+
"description": "OpenClaw bridge installed and managed by the AtMem memory control plane.",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"commandAliases": ["memory-atmem"],
|
|
7
|
+
"activation": {
|
|
8
|
+
"onStartup": true
|
|
9
|
+
},
|
|
10
|
+
"contracts": {
|
|
11
|
+
"tools": [
|
|
12
|
+
"memory_search",
|
|
13
|
+
"memory_get",
|
|
14
|
+
"memory_remember",
|
|
15
|
+
"atmem_search",
|
|
16
|
+
"atmem_forget",
|
|
17
|
+
"atmem_observe",
|
|
18
|
+
"atmem_forget_artifact"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"configSchema": {
|
|
22
|
+
"type": "object",
|
|
23
|
+
"additionalProperties": true,
|
|
24
|
+
"properties": {
|
|
25
|
+
"command": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"default": "atmem",
|
|
28
|
+
"description": "Executable that serves the engine (absolute path if not on PATH, e.g. /path/to/venv/bin/atmem). Use a python binary together with commandArgs [\"-m\",\"atmem.cli\",\"mcp\",...] as an alternative."
|
|
29
|
+
},
|
|
30
|
+
"commandArgs": {
|
|
31
|
+
"type": "array",
|
|
32
|
+
"items": { "type": "string" },
|
|
33
|
+
"description": "Override the full argument list. Default: [\"mcp\", \"--db\", <dbPath>, \"--subject\", <subject>]"
|
|
34
|
+
},
|
|
35
|
+
"dbPath": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"default": "~/.atmem/memories.db",
|
|
38
|
+
"description": "SQLite database path (also auditable from outside OpenClaw via `atmem verify`)"
|
|
39
|
+
},
|
|
40
|
+
"subject": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"default": "default",
|
|
43
|
+
"description": "Subject id used for all memory operations (single-user assistant)"
|
|
44
|
+
},
|
|
45
|
+
"takeoverActive": {
|
|
46
|
+
"type": "boolean",
|
|
47
|
+
"default": false,
|
|
48
|
+
"description": "Set only by verified native-memory takeover. Adds system guidance requiring memory_search/memory_get and forbidding direct MEMORY.md or memory/* filesystem access."
|
|
49
|
+
},
|
|
50
|
+
"nativeWorkspace": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"description": "Absolute OpenClaw workspace path protected during verified takeover. Set by `atmem control activate`; do not configure manually."
|
|
53
|
+
},
|
|
54
|
+
"recall": {
|
|
55
|
+
"type": "object",
|
|
56
|
+
"description": "Auto-recall injection before each prompt",
|
|
57
|
+
"properties": {
|
|
58
|
+
"enabled": { "type": "boolean", "default": true },
|
|
59
|
+
"maxRecords": { "type": "number", "default": 3, "description": "Max memories injected per turn" },
|
|
60
|
+
"maxChars": { "type": "number", "default": 1200, "description": "Total character budget for the injected block" },
|
|
61
|
+
"minScore": { "type": "number", "default": 0.3, "description": "Minimum relevance score; 0.3 requires a lexical match (priors alone never inject)" },
|
|
62
|
+
"timeoutMs": { "type": "number", "default": 4000, "description": "Recall timeout; on timeout the turn proceeds without injection" }
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"persona": {
|
|
66
|
+
"type": "object",
|
|
67
|
+
"description": "L3 persona snapshot injected alongside recall",
|
|
68
|
+
"properties": {
|
|
69
|
+
"enabled": { "type": "boolean", "default": true },
|
|
70
|
+
"maxChars": { "type": "number", "default": 600, "description": "Character budget for the <user_persona> block" },
|
|
71
|
+
"ttlSeconds": { "type": "number", "default": 300, "description": "Persona cache lifetime; also invalidated whenever new memory is captured" }
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"capture": {
|
|
75
|
+
"type": "object",
|
|
76
|
+
"description": "Auto-capture after each turn",
|
|
77
|
+
"properties": {
|
|
78
|
+
"enabled": { "type": "boolean", "default": true },
|
|
79
|
+
"captureAssistant": { "type": "boolean", "default": true, "description": "Log assistant replies to the audit chain (digest only, never stored as memory)" }
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"cacheAware": {
|
|
83
|
+
"type": "object",
|
|
84
|
+
"description": "Preserve a stable cacheable system prefix and place query-specific recall at the prompt tail",
|
|
85
|
+
"properties": {
|
|
86
|
+
"enabled": { "type": "boolean", "default": false, "description": "Use appendSystemContext for persona and appendContext for recall; false preserves pre-0.2.4 layout" },
|
|
87
|
+
"compactReferences": { "type": "boolean", "default": true, "description": "Use short model-visible references while full record IDs remain in audit evidence" }
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"tools": {
|
|
91
|
+
"type": "object",
|
|
92
|
+
"description": "Agent-callable explicit memory tools; automatic recall/capture continue when disabled",
|
|
93
|
+
"properties": {
|
|
94
|
+
"enabled": { "type": "boolean", "default": true, "description": "Register atmem_search and atmem_forget tool schemas" }
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"controlPlane": {
|
|
98
|
+
"type": "object",
|
|
99
|
+
"description": "Reversible memory control plane. Shadow mode does not change model context; active mode replaces native supplemental memory.",
|
|
100
|
+
"properties": {
|
|
101
|
+
"enabled": {
|
|
102
|
+
"type": "boolean",
|
|
103
|
+
"default": false,
|
|
104
|
+
"description": "Route hooks through the private memory control plane protocol; agent-callable memory tools are disabled in shadow mode."
|
|
105
|
+
},
|
|
106
|
+
"statePath": {
|
|
107
|
+
"type": "string",
|
|
108
|
+
"default": "~/.atmem/control-plane.json",
|
|
109
|
+
"description": "Digest-bound control file written by `atmem control`."
|
|
110
|
+
},
|
|
111
|
+
"blackboxEnabled": {
|
|
112
|
+
"type": "boolean",
|
|
113
|
+
"default": false,
|
|
114
|
+
"description": "Record content-minimizing agent flights through the private control protocol. Managed automatically by the AtMem installer."
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "openclaw-memory-atmem",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "OpenClaw Agent Black Box and memory control-plane bridge for AtMem",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"openclaw.plugin.json",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
12
|
+
"openclaw": {
|
|
13
|
+
"extensions": [
|
|
14
|
+
"./dist/index.js"
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=22.12.0"
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/aetna000/atmem.git",
|
|
23
|
+
"directory": "integrations/openclaw"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/aetna000/atmem/tree/main/integrations/openclaw",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/aetna000/atmem/issues"
|
|
28
|
+
},
|
|
29
|
+
"license": "AGPL-3.0-only",
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"openclaw": ">=2026.7.1-2"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsc -p tsconfig.json",
|
|
38
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
39
|
+
"test": "node test/setup.mjs && node test/hooks.mjs",
|
|
40
|
+
"smoke": "node test/smoke.mjs",
|
|
41
|
+
"prepack": "npm run build && npm run typecheck && npm test && npm run smoke"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/node": "^22.20.1",
|
|
45
|
+
"typescript": "5.6"
|
|
46
|
+
}
|
|
47
|
+
}
|