@totalreclaw/totalreclaw 1.3.0 → 1.4.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 +2 -2
- package/index.ts +135 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -79,9 +79,9 @@ Most of the time you won't use these directly -- the automatic hooks handle memo
|
|
|
79
79
|
| Tier | Memories | Reads | Storage | Price |
|
|
80
80
|
|------|----------|-------|---------|-------|
|
|
81
81
|
| **Free** | 500/month | Unlimited | Testnet (trial) | $0 |
|
|
82
|
-
| **Pro** | Unlimited | Unlimited | Permanent on-chain (Gnosis) |
|
|
82
|
+
| **Pro** | Unlimited | Unlimited | Permanent on-chain (Gnosis) | See `totalreclaw_status` |
|
|
83
83
|
|
|
84
|
-
Pay with card via Stripe. Counter resets monthly.
|
|
84
|
+
Pay with card via Stripe. Use `totalreclaw_status` to check current pricing. Counter resets monthly.
|
|
85
85
|
|
|
86
86
|
## Using with Other Agents
|
|
87
87
|
|
package/index.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* - totalreclaw_status -- check billing/subscription status
|
|
10
10
|
* - totalreclaw_consolidate -- scan and merge near-duplicate memories
|
|
11
11
|
* - totalreclaw_import_from -- import memories from other tools (Mem0, MCP Memory, etc.)
|
|
12
|
+
* - totalreclaw_upgrade -- create Stripe checkout for Pro upgrade
|
|
12
13
|
*
|
|
13
14
|
* Also registers a `before_agent_start` hook that automatically injects
|
|
14
15
|
* relevant memories into the agent's context.
|
|
@@ -134,6 +135,9 @@ const MAX_FACTS_PER_EXTRACTION = 15;
|
|
|
134
135
|
// Store-time near-duplicate detection (consolidation module)
|
|
135
136
|
const STORE_DEDUP_ENABLED = process.env.TOTALRECLAW_STORE_DEDUP !== 'false';
|
|
136
137
|
|
|
138
|
+
// One-time welcome-back message for returning Pro users (set during init, consumed by first before_agent_start)
|
|
139
|
+
let welcomeBackMessage: string | null = null;
|
|
140
|
+
|
|
137
141
|
// B2: Minimum relevance threshold — cosine below this means no memory injection
|
|
138
142
|
const RELEVANCE_THRESHOLD = parseFloat(process.env.TOTALRECLAW_RELEVANCE_THRESHOLD ?? '0.3');
|
|
139
143
|
|
|
@@ -421,6 +425,45 @@ async function initialize(logger: OpenClawPluginApi['logger']): Promise<void> {
|
|
|
421
425
|
subgraphOwner = userId;
|
|
422
426
|
}
|
|
423
427
|
}
|
|
428
|
+
|
|
429
|
+
// One-time billing check for returning users (imported recovery phrase).
|
|
430
|
+
// If they already have an active Pro subscription, inform them on next conversation start.
|
|
431
|
+
if (existingUserId && authKeyHex) {
|
|
432
|
+
try {
|
|
433
|
+
const walletAddr = subgraphOwner || userId || '';
|
|
434
|
+
if (walletAddr) {
|
|
435
|
+
const billingUrl = (process.env.TOTALRECLAW_SERVER_URL || 'https://api.totalreclaw.xyz').replace(/\/+$/, '');
|
|
436
|
+
const resp = await fetch(`${billingUrl}/v1/billing/status?wallet_address=${encodeURIComponent(walletAddr)}`, {
|
|
437
|
+
method: 'GET',
|
|
438
|
+
headers: {
|
|
439
|
+
'Authorization': `Bearer ${authKeyHex}`,
|
|
440
|
+
'Accept': 'application/json',
|
|
441
|
+
'X-TotalReclaw-Client': 'openclaw-plugin',
|
|
442
|
+
},
|
|
443
|
+
});
|
|
444
|
+
if (resp.ok) {
|
|
445
|
+
const billingData = await resp.json() as Record<string, unknown>;
|
|
446
|
+
const tier = billingData.tier as string;
|
|
447
|
+
const expiresAt = billingData.expires_at as string | undefined;
|
|
448
|
+
// Populate billing cache for future use.
|
|
449
|
+
writeBillingCache({
|
|
450
|
+
tier: tier || 'free',
|
|
451
|
+
free_writes_used: (billingData.free_writes_used as number) ?? 0,
|
|
452
|
+
free_writes_limit: (billingData.free_writes_limit as number) ?? 0,
|
|
453
|
+
features: billingData.features as BillingCache['features'] | undefined,
|
|
454
|
+
checked_at: Date.now(),
|
|
455
|
+
});
|
|
456
|
+
if (tier === 'pro' && expiresAt) {
|
|
457
|
+
const expiryDate = new Date(expiresAt).toLocaleDateString();
|
|
458
|
+
welcomeBackMessage = `Welcome back! Your Pro subscription is active (expires: ${expiryDate}).`;
|
|
459
|
+
logger.info(`Returning Pro user detected — expires ${expiryDate}`);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
} catch {
|
|
464
|
+
// Best-effort — don't block initialization on billing check failure.
|
|
465
|
+
}
|
|
466
|
+
}
|
|
424
467
|
}
|
|
425
468
|
|
|
426
469
|
function isDocker(): boolean {
|
|
@@ -2183,6 +2226,85 @@ const plugin = {
|
|
|
2183
2226
|
{ name: 'totalreclaw_import_from' },
|
|
2184
2227
|
);
|
|
2185
2228
|
|
|
2229
|
+
// ---------------------------------------------------------------
|
|
2230
|
+
// Tool: totalreclaw_upgrade
|
|
2231
|
+
// ---------------------------------------------------------------
|
|
2232
|
+
|
|
2233
|
+
api.registerTool(
|
|
2234
|
+
{
|
|
2235
|
+
name: 'totalreclaw_upgrade',
|
|
2236
|
+
label: 'Upgrade to Pro',
|
|
2237
|
+
description:
|
|
2238
|
+
'Upgrade to TotalReclaw Pro for unlimited encrypted memories. ' +
|
|
2239
|
+
'Returns a Stripe checkout URL for the user to complete payment via credit/debit card.',
|
|
2240
|
+
parameters: {
|
|
2241
|
+
type: 'object',
|
|
2242
|
+
properties: {},
|
|
2243
|
+
additionalProperties: false,
|
|
2244
|
+
},
|
|
2245
|
+
async execute() {
|
|
2246
|
+
try {
|
|
2247
|
+
await requireFullSetup(api.logger);
|
|
2248
|
+
|
|
2249
|
+
if (!authKeyHex) {
|
|
2250
|
+
return {
|
|
2251
|
+
content: [{ type: 'text', text: 'Auth credentials are not available. Please initialize first.' }],
|
|
2252
|
+
};
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2255
|
+
const serverUrl = (process.env.TOTALRECLAW_SERVER_URL || 'https://api.totalreclaw.xyz').replace(/\/+$/, '');
|
|
2256
|
+
const walletAddr = subgraphOwner || userId || '';
|
|
2257
|
+
|
|
2258
|
+
if (!walletAddr) {
|
|
2259
|
+
return {
|
|
2260
|
+
content: [{ type: 'text', text: 'Wallet address not available. Please ensure the plugin is fully initialized.' }],
|
|
2261
|
+
};
|
|
2262
|
+
}
|
|
2263
|
+
|
|
2264
|
+
const response = await fetch(`${serverUrl}/v1/billing/checkout`, {
|
|
2265
|
+
method: 'POST',
|
|
2266
|
+
headers: {
|
|
2267
|
+
'Authorization': `Bearer ${authKeyHex}`,
|
|
2268
|
+
'Content-Type': 'application/json',
|
|
2269
|
+
'X-TotalReclaw-Client': 'openclaw-plugin',
|
|
2270
|
+
},
|
|
2271
|
+
body: JSON.stringify({
|
|
2272
|
+
wallet_address: walletAddr,
|
|
2273
|
+
tier: 'pro',
|
|
2274
|
+
}),
|
|
2275
|
+
});
|
|
2276
|
+
|
|
2277
|
+
if (!response.ok) {
|
|
2278
|
+
const body = await response.text().catch(() => '');
|
|
2279
|
+
return {
|
|
2280
|
+
content: [{ type: 'text', text: `Failed to create checkout session (HTTP ${response.status}): ${body || response.statusText}` }],
|
|
2281
|
+
};
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2284
|
+
const data = await response.json() as { checkout_url?: string };
|
|
2285
|
+
|
|
2286
|
+
if (!data.checkout_url) {
|
|
2287
|
+
return {
|
|
2288
|
+
content: [{ type: 'text', text: 'Failed to create checkout session: no checkout URL returned.' }],
|
|
2289
|
+
};
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
return {
|
|
2293
|
+
content: [{ type: 'text', text: `Open this URL to upgrade to Pro: ${data.checkout_url}` }],
|
|
2294
|
+
details: { checkout_url: data.checkout_url },
|
|
2295
|
+
};
|
|
2296
|
+
} catch (err: unknown) {
|
|
2297
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2298
|
+
api.logger.error(`totalreclaw_upgrade failed: ${message}`);
|
|
2299
|
+
return {
|
|
2300
|
+
content: [{ type: 'text', text: `Failed to create checkout session: ${message}` }],
|
|
2301
|
+
};
|
|
2302
|
+
}
|
|
2303
|
+
},
|
|
2304
|
+
},
|
|
2305
|
+
{ name: 'totalreclaw_upgrade' },
|
|
2306
|
+
);
|
|
2307
|
+
|
|
2186
2308
|
// ---------------------------------------------------------------
|
|
2187
2309
|
// Hook: before_agent_start
|
|
2188
2310
|
// ---------------------------------------------------------------
|
|
@@ -2213,6 +2335,13 @@ const plugin = {
|
|
|
2213
2335
|
};
|
|
2214
2336
|
}
|
|
2215
2337
|
|
|
2338
|
+
// One-time welcome-back message for returning Pro users.
|
|
2339
|
+
let welcomeBack = '';
|
|
2340
|
+
if (welcomeBackMessage) {
|
|
2341
|
+
welcomeBack = `\n\n${welcomeBackMessage}`;
|
|
2342
|
+
welcomeBackMessage = null; // Consume — only show once
|
|
2343
|
+
}
|
|
2344
|
+
|
|
2216
2345
|
// Billing cache check — warn if quota is approaching limit.
|
|
2217
2346
|
let billingWarning = '';
|
|
2218
2347
|
try {
|
|
@@ -2286,7 +2415,7 @@ const plugin = {
|
|
|
2286
2415
|
const lines = cachedFacts.slice(0, 8).map((f, i) =>
|
|
2287
2416
|
`${i + 1}. ${f.text} (importance: ${f.importance}/10, cached)`,
|
|
2288
2417
|
);
|
|
2289
|
-
return { prependContext: `## Relevant Memories\n\n${lines.join('\n')}` + billingWarning };
|
|
2418
|
+
return { prependContext: `## Relevant Memories\n\n${lines.join('\n')}` + welcomeBack + billingWarning };
|
|
2290
2419
|
}
|
|
2291
2420
|
}
|
|
2292
2421
|
|
|
@@ -2299,7 +2428,7 @@ const plugin = {
|
|
|
2299
2428
|
const lines = cachedFacts.slice(0, 8).map((f, i) =>
|
|
2300
2429
|
`${i + 1}. ${f.text} (importance: ${f.importance}/10, cached)`,
|
|
2301
2430
|
);
|
|
2302
|
-
return { prependContext: `## Relevant Memories\n\n${lines.join('\n')}` + billingWarning };
|
|
2431
|
+
return { prependContext: `## Relevant Memories\n\n${lines.join('\n')}` + welcomeBack + billingWarning };
|
|
2303
2432
|
}
|
|
2304
2433
|
|
|
2305
2434
|
if (allTrapdoors.length === 0) return undefined;
|
|
@@ -2316,7 +2445,7 @@ const plugin = {
|
|
|
2316
2445
|
const lines = cachedFacts.slice(0, 8).map((f, i) =>
|
|
2317
2446
|
`${i + 1}. ${f.text} (importance: ${f.importance}/10, cached)`,
|
|
2318
2447
|
);
|
|
2319
|
-
return { prependContext: `## Relevant Memories\n\n${lines.join('\n')}` + billingWarning };
|
|
2448
|
+
return { prependContext: `## Relevant Memories\n\n${lines.join('\n')}` + welcomeBack + billingWarning };
|
|
2320
2449
|
}
|
|
2321
2450
|
return undefined;
|
|
2322
2451
|
}
|
|
@@ -2328,7 +2457,7 @@ const plugin = {
|
|
|
2328
2457
|
const lines = cachedFacts.slice(0, 8).map((f, i) =>
|
|
2329
2458
|
`${i + 1}. ${f.text} (importance: ${f.importance}/10, cached)`,
|
|
2330
2459
|
);
|
|
2331
|
-
return { prependContext: `## Relevant Memories\n\n${lines.join('\n')}` + billingWarning };
|
|
2460
|
+
return { prependContext: `## Relevant Memories\n\n${lines.join('\n')}` + welcomeBack + billingWarning };
|
|
2332
2461
|
}
|
|
2333
2462
|
|
|
2334
2463
|
// 5. Decrypt subgraph results and build reranker input.
|
|
@@ -2434,7 +2563,7 @@ const plugin = {
|
|
|
2434
2563
|
});
|
|
2435
2564
|
const contextString = `## Relevant Memories\n\n${lines.join('\n')}`;
|
|
2436
2565
|
|
|
2437
|
-
return { prependContext: contextString + billingWarning };
|
|
2566
|
+
return { prependContext: contextString + welcomeBack + billingWarning };
|
|
2438
2567
|
}
|
|
2439
2568
|
|
|
2440
2569
|
// --- Server mode (existing behavior) ---
|
|
@@ -2546,7 +2675,7 @@ const plugin = {
|
|
|
2546
2675
|
});
|
|
2547
2676
|
const contextString = `## Relevant Memories\n\n${lines.join('\n')}`;
|
|
2548
2677
|
|
|
2549
|
-
return { prependContext: contextString + billingWarning };
|
|
2678
|
+
return { prependContext: contextString + welcomeBack + billingWarning };
|
|
2550
2679
|
} catch (err: unknown) {
|
|
2551
2680
|
// The hook must NEVER throw -- log and return undefined.
|
|
2552
2681
|
const message = err instanceof Error ? err.message : String(err);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@totalreclaw/totalreclaw",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "End-to-end encrypted memory for AI agents — portable, yours forever. Automatic extraction, semantic search, and on-chain storage",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|