@totalreclaw/totalreclaw 1.0.1 → 1.0.3

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,26 @@
1
+ {
2
+ "id": "totalreclaw",
3
+ "name": "TotalReclaw",
4
+ "kind": "memory",
5
+ "description": "Zero-knowledge encrypted memory vault for AI agents",
6
+ "configSchema": {
7
+ "type": "object",
8
+ "properties": {
9
+ "extraction": {
10
+ "type": "object",
11
+ "properties": {
12
+ "model": {
13
+ "type": "string",
14
+ "description": "Override the extraction model (e.g., 'glm-4.5-flash', 'gpt-4.1-mini')"
15
+ },
16
+ "enabled": {
17
+ "type": "boolean",
18
+ "description": "Enable/disable auto-extraction (default: true)"
19
+ }
20
+ },
21
+ "additionalProperties": false
22
+ }
23
+ },
24
+ "additionalProperties": false
25
+ }
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@totalreclaw/totalreclaw",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Zero-knowledge encrypted memory vault for AI agents — the password manager for AI memory.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -21,17 +21,31 @@
21
21
  "ai",
22
22
  "agent"
23
23
  ],
24
- "dependencies": {
25
- "@huggingface/transformers": "^3.8.1",
24
+ "scripts": {
25
+ "build": "node esbuild.config.js",
26
+ "prepublishOnly": "npm run build"
27
+ },
28
+ "files": [
29
+ "dist/",
30
+ "openclaw.plugin.json",
31
+ "SKILL.md",
32
+ "README.md",
33
+ "porter-stemmer.d.ts"
34
+ ],
35
+ "devDependencies": {
36
+ "esbuild": "^0.27.3",
26
37
  "@noble/hashes": "^2.0.1",
27
38
  "@scure/bip39": "^2.0.1",
28
39
  "permissionless": "^0.3.4",
29
40
  "porter-stemmer": "^0.9.1",
30
41
  "viem": "^2.46.3"
31
42
  },
43
+ "optionalDependencies": {
44
+ "@huggingface/transformers": "^3.8.1"
45
+ },
32
46
  "openclaw": {
33
47
  "extensions": [
34
- "./index.ts"
48
+ "./dist/index.js"
35
49
  ]
36
50
  },
37
51
  "engines": {
@@ -1,27 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- push:
5
- branches: [main]
6
- pull_request:
7
- branches: [main]
8
-
9
- jobs:
10
- test:
11
- runs-on: ubuntu-latest
12
- steps:
13
- - uses: actions/checkout@v4
14
-
15
- - uses: actions/setup-node@v4
16
- with:
17
- node-version: 22
18
-
19
- - name: Install dependencies
20
- run: npm install
21
-
22
- - name: Type check
23
- run: npx tsc --noEmit --esModuleInterop --moduleResolution bundler --module nodenext --target es2022 --strict --skipLibCheck *.ts
24
- continue-on-error: true
25
-
26
- - name: Run tests
27
- run: npx vitest run --reporter=verbose 2>/dev/null || echo "No test runner configured — skipping"
@@ -1,41 +0,0 @@
1
- name: Publish to npm
2
-
3
- on:
4
- push:
5
- branches: [main]
6
-
7
- jobs:
8
- publish:
9
- runs-on: ubuntu-latest
10
- permissions:
11
- contents: read
12
- id-token: write
13
- steps:
14
- - uses: actions/checkout@v4
15
-
16
- - uses: actions/setup-node@v4
17
- with:
18
- node-version: 22
19
- registry-url: https://registry.npmjs.org
20
-
21
- - name: Install dependencies
22
- run: npm install
23
-
24
- - name: Check if version already published
25
- id: check
26
- run: |
27
- PKG_NAME=$(node -p "require('./package.json').name")
28
- PKG_VERSION=$(node -p "require('./package.json').version")
29
- if npm view "${PKG_NAME}@${PKG_VERSION}" version 2>/dev/null; then
30
- echo "exists=true" >> "$GITHUB_OUTPUT"
31
- echo "Version ${PKG_VERSION} already published — skipping"
32
- else
33
- echo "exists=false" >> "$GITHUB_OUTPUT"
34
- echo "Version ${PKG_VERSION} not yet published — will publish"
35
- fi
36
-
37
- - name: Publish
38
- if: steps.check.outputs.exists == 'false'
39
- run: npm publish --provenance --access public
40
- env:
41
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
package/api-client.ts DELETED
@@ -1,300 +0,0 @@
1
- /**
2
- * TotalReclaw Plugin - HTTP API Client
3
- *
4
- * Communicates with the TotalReclaw server over JSON/HTTP. Uses Node.js
5
- * built-in `fetch` (available since Node 18).
6
- *
7
- * All authenticated endpoints expect:
8
- * Authorization: Bearer <hex-encoded-auth-key>
9
- *
10
- * The server hashes the auth key with SHA-256 to look up the user.
11
- */
12
-
13
- // ---------------------------------------------------------------------------
14
- // Request / Response Types
15
- // ---------------------------------------------------------------------------
16
-
17
- /**
18
- * A single fact payload for the `/v1/store` endpoint.
19
- *
20
- * Field naming matches the server's `FactJSON` Pydantic model in
21
- * `server/src/handlers/store.py`.
22
- */
23
- export interface StoreFactPayload {
24
- /** UUIDv7 fact identifier */
25
- id: string;
26
- /** ISO 8601 timestamp */
27
- timestamp: string;
28
- /** Hex-encoded AES-256-GCM ciphertext (iv || tag || ciphertext) */
29
- encrypted_blob: string;
30
- /** SHA-256 hashes of tokens for blind search */
31
- blind_indices: string[];
32
- /** Importance / decay score (0-10) */
33
- decay_score: number;
34
- /** Origin label */
35
- source: string;
36
- /** HMAC-SHA256 content fingerprint for dedup (hex) */
37
- content_fp?: string;
38
- /** Identifier of the creating agent */
39
- agent_id?: string;
40
- /** Hex-encoded AES-256-GCM encrypted embedding vector (PoC v2) */
41
- encrypted_embedding?: string;
42
- }
43
-
44
- /**
45
- * A search result candidate returned by `/v1/search`.
46
- *
47
- * Field naming matches the server's `SearchResultJSON` model.
48
- */
49
- export interface SearchCandidate {
50
- fact_id: string;
51
- /** Hex-encoded AES-256-GCM ciphertext */
52
- encrypted_blob: string;
53
- decay_score: number;
54
- /** Unix milliseconds */
55
- timestamp: number;
56
- version: number;
57
- /** Hex-encoded AES-256-GCM encrypted embedding vector (PoC v2, optional) */
58
- encrypted_embedding?: string;
59
- }
60
-
61
- /**
62
- * A fact object returned by `/v1/export`.
63
- */
64
- export interface ExportedFact {
65
- id: string;
66
- encrypted_blob: string;
67
- blind_indices: string[];
68
- decay_score: number;
69
- version: number;
70
- source: string;
71
- created_at: string;
72
- updated_at: string;
73
- }
74
-
75
- // ---------------------------------------------------------------------------
76
- // API Client Factory
77
- // ---------------------------------------------------------------------------
78
-
79
- /**
80
- * Create an API client bound to a specific TotalReclaw server URL.
81
- *
82
- * All methods are async and throw descriptive errors on non-2xx responses.
83
- */
84
- export function createApiClient(serverUrl: string) {
85
- // Normalise URL -- strip trailing slash.
86
- const baseUrl = serverUrl.replace(/\/+$/, '');
87
-
88
- // ------------------------------------------------------------------
89
- // Shared helpers
90
- // ------------------------------------------------------------------
91
-
92
- /**
93
- * Throw a descriptive error when the server returns a non-2xx status.
94
- */
95
- async function assertOk(res: Response, context: string): Promise<void> {
96
- if (res.ok) return;
97
- let body: string;
98
- try {
99
- body = await res.text();
100
- } catch {
101
- body = '(could not read response body)';
102
- }
103
- const hint = res.status === 401
104
- ? ' Authentication failed. If using a recovery phrase, check that all 12 words are in the correct order and spelled correctly.'
105
- : '';
106
- throw new Error(`${context}: HTTP ${res.status} - ${body}${hint}`);
107
- }
108
-
109
- // ------------------------------------------------------------------
110
- // Public methods
111
- // ------------------------------------------------------------------
112
-
113
- return {
114
- // ---- Registration (unauthenticated) ----
115
-
116
- /**
117
- * Register a new user.
118
- *
119
- * @param authKeyHash Hex-encoded SHA-256 of the auth key (64 chars).
120
- * @param saltHex Hex-encoded 32-byte salt (64 chars).
121
- * @returns `{ user_id }` on success.
122
- */
123
- async register(
124
- authKeyHash: string,
125
- saltHex: string,
126
- ): Promise<{ user_id: string }> {
127
- const res = await fetch(`${baseUrl}/v1/register`, {
128
- method: 'POST',
129
- headers: { 'Content-Type': 'application/json' },
130
- body: JSON.stringify({ auth_key_hash: authKeyHash, salt: saltHex }),
131
- });
132
- await assertOk(res, 'register');
133
- const json = (await res.json()) as Record<string, unknown>;
134
- if (!json.success && json.error_code !== 'USER_EXISTS') {
135
- throw new Error(
136
- `register: server returned success=false - ${json.error_code}: ${json.error_message}`,
137
- );
138
- }
139
- if (!json.user_id) {
140
- throw new Error(
141
- `register: server did not return user_id (error_code=${json.error_code})`,
142
- );
143
- }
144
- return { user_id: json.user_id as string };
145
- },
146
-
147
- // ---- Store (authenticated) ----
148
-
149
- /**
150
- * Store one or more encrypted facts.
151
- *
152
- * @param userId The authenticated user's ID.
153
- * @param facts Array of `StoreFactPayload` objects.
154
- * @param authKeyHex Hex-encoded raw auth key (64 chars) for Bearer header.
155
- */
156
- async store(
157
- userId: string,
158
- facts: StoreFactPayload[],
159
- authKeyHex: string,
160
- ): Promise<{ ids: string[]; duplicate_ids?: string[] }> {
161
- const res = await fetch(`${baseUrl}/v1/store`, {
162
- method: 'POST',
163
- headers: {
164
- 'Content-Type': 'application/json',
165
- Authorization: `Bearer ${authKeyHex}`,
166
- },
167
- body: JSON.stringify({ user_id: userId, facts }),
168
- });
169
- await assertOk(res, 'store');
170
- const json = (await res.json()) as Record<string, unknown>;
171
- if (!json.success) {
172
- throw new Error(
173
- `store: server returned success=false - ${json.error_code}: ${json.error_message}`,
174
- );
175
- }
176
- return {
177
- ids: (json.ids as string[]) ?? [],
178
- duplicate_ids: json.duplicate_ids as string[] | undefined,
179
- };
180
- },
181
-
182
- // ---- Search (authenticated) ----
183
-
184
- /**
185
- * Search for facts using blind trapdoors.
186
- *
187
- * @param userId The authenticated user's ID.
188
- * @param trapdoors SHA-256 hex hashes of query tokens.
189
- * @param maxCandidates Maximum candidates to retrieve.
190
- * @param authKeyHex Hex-encoded raw auth key for Bearer header.
191
- * @returns Array of encrypted search candidates.
192
- */
193
- async search(
194
- userId: string,
195
- trapdoors: string[],
196
- maxCandidates: number,
197
- authKeyHex: string,
198
- ): Promise<SearchCandidate[]> {
199
- const res = await fetch(`${baseUrl}/v1/search`, {
200
- method: 'POST',
201
- headers: {
202
- 'Content-Type': 'application/json',
203
- Authorization: `Bearer ${authKeyHex}`,
204
- },
205
- body: JSON.stringify({
206
- user_id: userId,
207
- trapdoors,
208
- max_candidates: maxCandidates,
209
- }),
210
- });
211
- await assertOk(res, 'search');
212
- const json = (await res.json()) as Record<string, unknown>;
213
- if (!json.success) {
214
- throw new Error(
215
- `search: server returned success=false - ${json.error_code}: ${json.error_message}`,
216
- );
217
- }
218
- return (json.results as SearchCandidate[]) ?? [];
219
- },
220
-
221
- // ---- Delete (authenticated) ----
222
-
223
- /**
224
- * Soft-delete a fact by ID.
225
- *
226
- * @param factId The fact UUID to delete.
227
- * @param authKeyHex Hex-encoded raw auth key for Bearer header.
228
- */
229
- async deleteFact(factId: string, authKeyHex: string): Promise<void> {
230
- const res = await fetch(`${baseUrl}/v1/facts/${encodeURIComponent(factId)}`, {
231
- method: 'DELETE',
232
- headers: {
233
- Authorization: `Bearer ${authKeyHex}`,
234
- },
235
- });
236
- await assertOk(res, 'deleteFact');
237
- const json = (await res.json()) as Record<string, unknown>;
238
- if (!json.success) {
239
- throw new Error(
240
- `deleteFact: server returned success=false - ${json.error_code}: ${json.error_message}`,
241
- );
242
- }
243
- },
244
-
245
- // ---- Export (authenticated) ----
246
-
247
- /**
248
- * Export all active facts (paginated).
249
- *
250
- * @param authKeyHex Hex-encoded raw auth key for Bearer header.
251
- * @param limit Page size (default 1000, max 5000).
252
- * @param cursor Cursor from previous page (omit for first page).
253
- * @returns Page of facts with pagination metadata.
254
- */
255
- async exportFacts(
256
- authKeyHex: string,
257
- limit: number = 1000,
258
- cursor?: string,
259
- ): Promise<{ facts: ExportedFact[]; cursor?: string; has_more: boolean; total_count?: number }> {
260
- const params = new URLSearchParams({ limit: String(limit) });
261
- if (cursor) params.set('cursor', cursor);
262
-
263
- const res = await fetch(`${baseUrl}/v1/export?${params.toString()}`, {
264
- method: 'GET',
265
- headers: {
266
- Authorization: `Bearer ${authKeyHex}`,
267
- },
268
- });
269
- await assertOk(res, 'exportFacts');
270
- const json = (await res.json()) as Record<string, unknown>;
271
- if (!json.success) {
272
- throw new Error(
273
- `exportFacts: server returned success=false - ${json.error_code}: ${json.error_message}`,
274
- );
275
- }
276
- return {
277
- facts: (json.facts as ExportedFact[]) ?? [],
278
- cursor: json.cursor as string | undefined,
279
- has_more: (json.has_more as boolean) ?? false,
280
- total_count: json.total_count as number | undefined,
281
- };
282
- },
283
-
284
- // ---- Health (unauthenticated) ----
285
-
286
- /**
287
- * Check server health.
288
- *
289
- * @returns `true` if the server responds with HTTP 200.
290
- */
291
- async health(): Promise<boolean> {
292
- try {
293
- const res = await fetch(`${baseUrl}/health`, { method: 'GET' });
294
- return res.status === 200;
295
- } catch {
296
- return false;
297
- }
298
- },
299
- };
300
- }