jasper-recall 0.3.6 → 0.4.1
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/SKILL.md +26 -0
- package/cli/jasper-recall.js +58 -11
- package/extensions/moltbook-setup/setup.js +433 -0
- package/package.json +1 -1
package/SKILL.md
CHANGED
|
@@ -197,6 +197,32 @@ recall "product info" --public-only
|
|
|
197
197
|
recall "product info"
|
|
198
198
|
```
|
|
199
199
|
|
|
200
|
+
### Moltbook Agent Setup (v0.4.0+)
|
|
201
|
+
|
|
202
|
+
For the moltbook-scanner (or any sandboxed agent), use the built-in setup:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
# Configure sandboxed agent with --public-only restriction
|
|
206
|
+
npx jasper-recall moltbook-setup
|
|
207
|
+
|
|
208
|
+
# Verify the setup is correct
|
|
209
|
+
npx jasper-recall moltbook-verify
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
This creates:
|
|
213
|
+
- `~/bin/recall` — Wrapper that forces `--public-only` flag
|
|
214
|
+
- `shared/` — Symlink to main workspace's shared memory
|
|
215
|
+
|
|
216
|
+
The sandboxed agent can then use:
|
|
217
|
+
```bash
|
|
218
|
+
~/bin/recall "query" # Automatically restricted to public memories
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Privacy model:**
|
|
222
|
+
1. Main agent tags memories as `[public]` or `[private]` in daily notes
|
|
223
|
+
2. `sync-shared` extracts `[public]` content to `memory/shared/`
|
|
224
|
+
3. Sandboxed agents can ONLY search the `shared` collection
|
|
225
|
+
|
|
200
226
|
### Privacy Workflow
|
|
201
227
|
|
|
202
228
|
```bash
|
package/cli/jasper-recall.js
CHANGED
|
@@ -198,6 +198,36 @@ function setup() {
|
|
|
198
198
|
console.log(' 1. index-digests # Index your memory files');
|
|
199
199
|
console.log(' 2. recall "query" # Search your memory');
|
|
200
200
|
console.log(' 3. digest-sessions # Process session logs');
|
|
201
|
+
console.log('');
|
|
202
|
+
|
|
203
|
+
// Check for sandboxed agents
|
|
204
|
+
const sandboxedWorkspaces = findSandboxedWorkspaces();
|
|
205
|
+
if (sandboxedWorkspaces.length > 0) {
|
|
206
|
+
console.log('📦 Sandboxed agents detected:');
|
|
207
|
+
sandboxedWorkspaces.forEach(ws => console.log(` - ${ws}`));
|
|
208
|
+
console.log('');
|
|
209
|
+
console.log(' Configure them with: npx jasper-recall sandboxed-setup');
|
|
210
|
+
console.log(' (gives them --public-only access to shared memories)');
|
|
211
|
+
console.log('');
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function findSandboxedWorkspaces() {
|
|
216
|
+
const openclawDir = path.join(os.homedir(), '.openclaw');
|
|
217
|
+
const workspaces = [];
|
|
218
|
+
|
|
219
|
+
try {
|
|
220
|
+
const entries = fs.readdirSync(openclawDir);
|
|
221
|
+
for (const entry of entries) {
|
|
222
|
+
if (entry.startsWith('workspace-') && entry !== 'workspace') {
|
|
223
|
+
workspaces.push(entry.replace('workspace-', ''));
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
} catch (e) {
|
|
227
|
+
// Directory doesn't exist
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return workspaces;
|
|
201
231
|
}
|
|
202
232
|
|
|
203
233
|
function showHelp() {
|
|
@@ -209,17 +239,19 @@ USAGE:
|
|
|
209
239
|
npx jasper-recall <command>
|
|
210
240
|
|
|
211
241
|
COMMANDS:
|
|
212
|
-
setup
|
|
213
|
-
doctor
|
|
214
|
-
|
|
215
|
-
recall
|
|
216
|
-
index
|
|
217
|
-
digest
|
|
218
|
-
summarize
|
|
219
|
-
serve
|
|
220
|
-
config
|
|
221
|
-
update
|
|
222
|
-
|
|
242
|
+
setup Install dependencies and CLI scripts
|
|
243
|
+
doctor Run system health check
|
|
244
|
+
Flags: --fix (auto-repair issues), --dry-run (verbose output)
|
|
245
|
+
recall Search your memory (alias for the recall command)
|
|
246
|
+
index Index memory files (alias for index-digests)
|
|
247
|
+
digest Process session logs (alias for digest-sessions)
|
|
248
|
+
summarize Compress old entries to save tokens (alias for summarize-old)
|
|
249
|
+
serve Start HTTP API server (for sandboxed agents)
|
|
250
|
+
config Show or set configuration
|
|
251
|
+
update Check for updates
|
|
252
|
+
sandboxed-setup Configure sandboxed agents (email, social, calendar, etc.)
|
|
253
|
+
sandboxed-verify Verify sandboxed agent configurations
|
|
254
|
+
help Show this help message
|
|
223
255
|
|
|
224
256
|
CONFIGURATION:
|
|
225
257
|
Config file: ~/.jasper-recall/config.json
|
|
@@ -311,6 +343,21 @@ switch (command) {
|
|
|
311
343
|
};
|
|
312
344
|
process.exit(runDoctor(options));
|
|
313
345
|
break;
|
|
346
|
+
case 'sandboxed-setup':
|
|
347
|
+
case 'sandbox-setup':
|
|
348
|
+
case 'moltbook-setup':
|
|
349
|
+
case 'moltbook':
|
|
350
|
+
// Interactive setup for sandboxed agents
|
|
351
|
+
process.argv = [process.argv[0], process.argv[1], 'setup'];
|
|
352
|
+
require('../extensions/moltbook-setup/setup.js');
|
|
353
|
+
break;
|
|
354
|
+
case 'sandboxed-verify':
|
|
355
|
+
case 'sandbox-verify':
|
|
356
|
+
case 'moltbook-verify':
|
|
357
|
+
// Verify sandboxed agent setups
|
|
358
|
+
process.argv = [process.argv[0], process.argv[1], 'verify'];
|
|
359
|
+
require('../extensions/moltbook-setup/setup.js');
|
|
360
|
+
break;
|
|
314
361
|
case 'config':
|
|
315
362
|
// Configuration management
|
|
316
363
|
const config = require('./config');
|
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Sandboxed Agent Setup for jasper-recall
|
|
4
|
+
*
|
|
5
|
+
* Configures sandboxed agents to use jasper-recall with --public-only restriction.
|
|
6
|
+
* This ensures agents can only access shared/public memories, not private ones.
|
|
7
|
+
*
|
|
8
|
+
* Use cases:
|
|
9
|
+
* - Moltbook scanner (social media engagement)
|
|
10
|
+
* - Email agent (inbox management)
|
|
11
|
+
* - Calendar agent (scheduling)
|
|
12
|
+
* - Any agent that shouldn't see private memories
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const os = require('os');
|
|
18
|
+
const readline = require('readline');
|
|
19
|
+
|
|
20
|
+
const OPENCLAW_DIR = path.join(os.homedir(), '.openclaw');
|
|
21
|
+
const MAIN_WORKSPACE = path.join(OPENCLAW_DIR, 'workspace');
|
|
22
|
+
const RECALL_BIN = path.join(os.homedir(), '.local', 'bin', 'recall');
|
|
23
|
+
|
|
24
|
+
function log(msg) {
|
|
25
|
+
console.log(`🔒 ${msg}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function warn(msg) {
|
|
29
|
+
console.log(`⚠️ ${msg}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function error(msg) {
|
|
33
|
+
console.error(`❌ ${msg}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function success(msg) {
|
|
37
|
+
console.log(`✅ ${msg}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function prompt(question) {
|
|
41
|
+
const rl = readline.createInterface({
|
|
42
|
+
input: process.stdin,
|
|
43
|
+
output: process.stdout
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
return new Promise(resolve => {
|
|
47
|
+
rl.question(question, answer => {
|
|
48
|
+
rl.close();
|
|
49
|
+
resolve(answer.trim());
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function interactiveSetup() {
|
|
55
|
+
console.log('');
|
|
56
|
+
log('Sandboxed Agent Setup — jasper-recall Integration');
|
|
57
|
+
console.log('='.repeat(60));
|
|
58
|
+
console.log('');
|
|
59
|
+
console.log(' This configures a sandboxed agent to use jasper-recall with');
|
|
60
|
+
console.log(' privacy restrictions (--public-only).');
|
|
61
|
+
console.log('');
|
|
62
|
+
console.log(' 🔒 Privacy: Sandboxed agents can ONLY see [public] memories.');
|
|
63
|
+
console.log(' They cannot access your private notes or secrets.');
|
|
64
|
+
console.log('');
|
|
65
|
+
|
|
66
|
+
// List existing workspaces
|
|
67
|
+
const workspaces = findAgentWorkspaces();
|
|
68
|
+
|
|
69
|
+
if (workspaces.length === 0) {
|
|
70
|
+
console.log(' No sandboxed agent workspaces found.');
|
|
71
|
+
console.log('');
|
|
72
|
+
console.log(' To create one, add an agent to your openclaw.json:');
|
|
73
|
+
console.log('');
|
|
74
|
+
showAgentExample();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
console.log(' Found agent workspaces:');
|
|
79
|
+
workspaces.forEach((ws, i) => {
|
|
80
|
+
const status = checkWorkspaceStatus(ws.path);
|
|
81
|
+
const statusIcon = status.configured ? '✅' : '⚪';
|
|
82
|
+
console.log(` ${i + 1}. ${statusIcon} ${ws.name} (${ws.path})`);
|
|
83
|
+
});
|
|
84
|
+
console.log('');
|
|
85
|
+
|
|
86
|
+
const choice = await prompt(' Configure which agent? (number, or "skip" to exit): ');
|
|
87
|
+
|
|
88
|
+
if (choice.toLowerCase() === 'skip' || choice === '') {
|
|
89
|
+
console.log('\n Skipped.\n');
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const index = parseInt(choice, 10) - 1;
|
|
94
|
+
if (isNaN(index) || index < 0 || index >= workspaces.length) {
|
|
95
|
+
error('Invalid selection');
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const selected = workspaces[index];
|
|
100
|
+
await setupWorkspace(selected.path, selected.name);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function findAgentWorkspaces() {
|
|
104
|
+
const workspaces = [];
|
|
105
|
+
|
|
106
|
+
// Look for workspace-* directories
|
|
107
|
+
try {
|
|
108
|
+
const entries = fs.readdirSync(OPENCLAW_DIR);
|
|
109
|
+
for (const entry of entries) {
|
|
110
|
+
if (entry.startsWith('workspace-') && entry !== 'workspace') {
|
|
111
|
+
const wsPath = path.join(OPENCLAW_DIR, entry);
|
|
112
|
+
if (fs.statSync(wsPath).isDirectory()) {
|
|
113
|
+
workspaces.push({
|
|
114
|
+
name: entry.replace('workspace-', ''),
|
|
115
|
+
path: wsPath
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
} catch (e) {
|
|
121
|
+
// Directory doesn't exist or not readable
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return workspaces;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function checkWorkspaceStatus(wsPath) {
|
|
128
|
+
const wrapperPath = path.join(wsPath, 'bin', 'recall');
|
|
129
|
+
const sharedPath = path.join(wsPath, 'shared');
|
|
130
|
+
|
|
131
|
+
let configured = false;
|
|
132
|
+
|
|
133
|
+
if (fs.existsSync(wrapperPath)) {
|
|
134
|
+
const content = fs.readFileSync(wrapperPath, 'utf8');
|
|
135
|
+
configured = content.includes('--public-only');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
configured,
|
|
140
|
+
hasWrapper: fs.existsSync(wrapperPath),
|
|
141
|
+
hasShared: fs.existsSync(sharedPath)
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function setupWorkspace(wsPath, name) {
|
|
146
|
+
console.log('');
|
|
147
|
+
log(`Configuring ${name}...`);
|
|
148
|
+
|
|
149
|
+
// Check prerequisites
|
|
150
|
+
if (!fs.existsSync(RECALL_BIN)) {
|
|
151
|
+
error(`jasper-recall not installed: ${RECALL_BIN}`);
|
|
152
|
+
console.log(' Run the main setup first: npx jasper-recall setup');
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Step 1: Create bin directory and wrapper
|
|
157
|
+
const binDir = path.join(wsPath, 'bin');
|
|
158
|
+
const wrapperPath = path.join(binDir, 'recall');
|
|
159
|
+
|
|
160
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
161
|
+
|
|
162
|
+
const wrapperScript = `#!/bin/bash
|
|
163
|
+
# Sandboxed recall wrapper - forces --public-only for privacy
|
|
164
|
+
# This agent can ONLY access shared/public memory
|
|
165
|
+
|
|
166
|
+
exec ${RECALL_BIN} "$@" --public-only
|
|
167
|
+
`;
|
|
168
|
+
|
|
169
|
+
fs.writeFileSync(wrapperPath, wrapperScript);
|
|
170
|
+
fs.chmodSync(wrapperPath, '755');
|
|
171
|
+
success(`Created recall wrapper: bin/recall`);
|
|
172
|
+
|
|
173
|
+
// Step 2: Create shared folder symlink
|
|
174
|
+
const sharedSource = path.join(MAIN_WORKSPACE, 'memory', 'shared');
|
|
175
|
+
const sharedTarget = path.join(wsPath, 'shared');
|
|
176
|
+
|
|
177
|
+
// Ensure source exists
|
|
178
|
+
fs.mkdirSync(sharedSource, { recursive: true });
|
|
179
|
+
|
|
180
|
+
// Remove existing symlink/dir if needed
|
|
181
|
+
try {
|
|
182
|
+
const stat = fs.lstatSync(sharedTarget);
|
|
183
|
+
if (stat.isSymbolicLink()) {
|
|
184
|
+
fs.unlinkSync(sharedTarget);
|
|
185
|
+
}
|
|
186
|
+
} catch (e) {
|
|
187
|
+
// Doesn't exist, that's fine
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (!fs.existsSync(sharedTarget)) {
|
|
191
|
+
fs.symlinkSync(sharedSource, sharedTarget);
|
|
192
|
+
success(`Created symlink: shared/ → main workspace`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Step 3: Check if AGENTS.md exists and suggest update
|
|
196
|
+
const agentsMd = path.join(wsPath, 'AGENTS.md');
|
|
197
|
+
if (fs.existsSync(agentsMd)) {
|
|
198
|
+
const content = fs.readFileSync(agentsMd, 'utf8');
|
|
199
|
+
if (!content.includes('public-only') && !content.includes('--public-only')) {
|
|
200
|
+
warn('Consider adding recall restrictions to AGENTS.md');
|
|
201
|
+
console.log('');
|
|
202
|
+
console.log(' Suggested addition:');
|
|
203
|
+
console.log(' ```');
|
|
204
|
+
console.log(' ## Memory Access');
|
|
205
|
+
console.log(' Use `~/bin/recall "query"` for memory search.');
|
|
206
|
+
console.log(' This wrapper enforces --public-only (you cannot see private memories).');
|
|
207
|
+
console.log(' ```');
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
console.log('');
|
|
212
|
+
success(`${name} configured!`);
|
|
213
|
+
console.log('');
|
|
214
|
+
console.log(' The agent can now use:');
|
|
215
|
+
console.log(` ~/bin/recall "query" — searches public memories only`);
|
|
216
|
+
console.log(` shared/ — symlink to shared memory folder`);
|
|
217
|
+
console.log('');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function showAgentExample() {
|
|
221
|
+
console.log(` Example openclaw.json agent config:
|
|
222
|
+
|
|
223
|
+
{
|
|
224
|
+
"agents": {
|
|
225
|
+
"list": [
|
|
226
|
+
{
|
|
227
|
+
"id": "email-agent",
|
|
228
|
+
"workspace": "~/.openclaw/workspace-email",
|
|
229
|
+
"model": { "primary": "anthropic/claude-sonnet-4-5" },
|
|
230
|
+
"sandbox": {
|
|
231
|
+
"mode": "all",
|
|
232
|
+
"workspaceAccess": "rw"
|
|
233
|
+
},
|
|
234
|
+
"tools": {
|
|
235
|
+
"profile": "minimal",
|
|
236
|
+
"allow": ["read", "write", "exec", "web_fetch"]
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
]
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
Common sandboxed agent use cases:
|
|
244
|
+
|
|
245
|
+
📧 Email Agent
|
|
246
|
+
- Checks inbox, drafts replies, summarizes threads
|
|
247
|
+
- Sandbox: Only email API access, no filesystem
|
|
248
|
+
- Memory: Sees [public] context about projects
|
|
249
|
+
|
|
250
|
+
📱 Social Agent (Moltbook, Twitter, etc.)
|
|
251
|
+
- Monitors feeds, engages with posts
|
|
252
|
+
- Sandbox: Only that platform's API
|
|
253
|
+
- Memory: Sees [public] product info for authentic engagement
|
|
254
|
+
|
|
255
|
+
📅 Calendar Agent
|
|
256
|
+
- Manages scheduling, sends reminders
|
|
257
|
+
- Sandbox: Only calendar API
|
|
258
|
+
- Memory: Sees [public] project timelines
|
|
259
|
+
|
|
260
|
+
🔍 Research Agent
|
|
261
|
+
- Web searches, summarizes articles
|
|
262
|
+
- Sandbox: Read-only web access
|
|
263
|
+
- Memory: Sees [public] research context
|
|
264
|
+
|
|
265
|
+
After creating the workspace, run:
|
|
266
|
+
npx jasper-recall sandboxed-setup
|
|
267
|
+
`);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function verify(wsPath, options = {}) {
|
|
271
|
+
const { quiet = false } = options;
|
|
272
|
+
const issues = [];
|
|
273
|
+
|
|
274
|
+
if (!quiet) {
|
|
275
|
+
console.log('');
|
|
276
|
+
log('Verifying sandboxed agent setup...');
|
|
277
|
+
console.log('');
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Check 1: Workspace exists
|
|
281
|
+
if (!fs.existsSync(wsPath)) {
|
|
282
|
+
issues.push(`Workspace missing: ${wsPath}`);
|
|
283
|
+
} else if (!quiet) {
|
|
284
|
+
success(`Workspace exists`);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Check 2: Recall wrapper exists and is correct
|
|
288
|
+
const wrapperPath = path.join(wsPath, 'bin', 'recall');
|
|
289
|
+
if (!fs.existsSync(wrapperPath)) {
|
|
290
|
+
issues.push(`Recall wrapper missing: bin/recall`);
|
|
291
|
+
} else {
|
|
292
|
+
const content = fs.readFileSync(wrapperPath, 'utf8');
|
|
293
|
+
if (!content.includes('--public-only')) {
|
|
294
|
+
issues.push('Recall wrapper missing --public-only flag!');
|
|
295
|
+
} else if (!quiet) {
|
|
296
|
+
success('Recall wrapper has --public-only restriction');
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Check 3: Shared folder is a symlink
|
|
301
|
+
const sharedPath = path.join(wsPath, 'shared');
|
|
302
|
+
try {
|
|
303
|
+
const stat = fs.lstatSync(sharedPath);
|
|
304
|
+
if (!stat.isSymbolicLink()) {
|
|
305
|
+
issues.push(`shared/ is not a symlink`);
|
|
306
|
+
} else if (!quiet) {
|
|
307
|
+
const target = fs.readlinkSync(sharedPath);
|
|
308
|
+
success(`shared/ → ${target}`);
|
|
309
|
+
}
|
|
310
|
+
} catch (e) {
|
|
311
|
+
issues.push(`shared/ folder missing`);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Check 4: jasper-recall is installed
|
|
315
|
+
if (!fs.existsSync(RECALL_BIN)) {
|
|
316
|
+
issues.push(`jasper-recall not installed`);
|
|
317
|
+
} else if (!quiet) {
|
|
318
|
+
success(`jasper-recall installed`);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (!quiet) {
|
|
322
|
+
console.log('');
|
|
323
|
+
if (issues.length === 0) {
|
|
324
|
+
success('All checks passed!');
|
|
325
|
+
} else {
|
|
326
|
+
warn(`Found ${issues.length} issue(s):`);
|
|
327
|
+
issues.forEach(issue => console.log(` ❌ ${issue}`));
|
|
328
|
+
console.log('');
|
|
329
|
+
console.log(' Run setup to fix: npx jasper-recall sandboxed-setup');
|
|
330
|
+
}
|
|
331
|
+
console.log('');
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return issues;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
async function verifyInteractive() {
|
|
338
|
+
const workspaces = findAgentWorkspaces();
|
|
339
|
+
|
|
340
|
+
if (workspaces.length === 0) {
|
|
341
|
+
console.log('');
|
|
342
|
+
warn('No sandboxed agent workspaces found.');
|
|
343
|
+
console.log('');
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
console.log('');
|
|
348
|
+
log('Sandboxed Agent Verification');
|
|
349
|
+
console.log('='.repeat(60));
|
|
350
|
+
|
|
351
|
+
for (const ws of workspaces) {
|
|
352
|
+
console.log('');
|
|
353
|
+
console.log(` 📁 ${ws.name}`);
|
|
354
|
+
const issues = verify(ws.path, { quiet: true });
|
|
355
|
+
if (issues.length === 0) {
|
|
356
|
+
console.log(` ✅ Properly configured`);
|
|
357
|
+
} else {
|
|
358
|
+
console.log(` ⚠️ ${issues.length} issue(s):`);
|
|
359
|
+
issues.forEach(issue => console.log(` - ${issue}`));
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
console.log('');
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function showHelp() {
|
|
367
|
+
console.log(`
|
|
368
|
+
Sandboxed Agent Setup — jasper-recall Integration
|
|
369
|
+
|
|
370
|
+
USAGE:
|
|
371
|
+
npx jasper-recall sandboxed-setup Interactive setup for any sandboxed agent
|
|
372
|
+
npx jasper-recall sandboxed-verify Check all sandboxed agents
|
|
373
|
+
npx jasper-recall moltbook-setup (alias) Setup for moltbook specifically
|
|
374
|
+
npx jasper-recall moltbook-verify (alias) Verify moltbook specifically
|
|
375
|
+
|
|
376
|
+
WHAT IT DOES:
|
|
377
|
+
Configures sandboxed agents to use jasper-recall with privacy restrictions.
|
|
378
|
+
Agents can only access [public] tagged memories, not private ones.
|
|
379
|
+
|
|
380
|
+
COMPONENTS CREATED:
|
|
381
|
+
bin/recall Wrapper script that forces --public-only flag
|
|
382
|
+
shared/ Symlink to main workspace's shared memory folder
|
|
383
|
+
|
|
384
|
+
USE CASES:
|
|
385
|
+
📧 Email Agent — inbox management, drafts, summaries
|
|
386
|
+
📱 Social Agent — moltbook, twitter engagement
|
|
387
|
+
📅 Calendar Agent — scheduling, reminders
|
|
388
|
+
🔍 Research Agent — web searches, article summaries
|
|
389
|
+
|
|
390
|
+
PRIVACY MODEL:
|
|
391
|
+
1. Main agent tags memories as [public] or [private] in daily notes
|
|
392
|
+
2. sync-shared extracts [public] content to memory/shared/
|
|
393
|
+
3. Sandboxed agents can ONLY search the shared collection
|
|
394
|
+
|
|
395
|
+
EXAMPLE:
|
|
396
|
+
# Main agent daily note
|
|
397
|
+
## 2026-02-11 [public] - Shipped jasper-recall v0.4.0
|
|
398
|
+
New sandboxed agent setup feature.
|
|
399
|
+
|
|
400
|
+
## 2026-02-11 [private] - Personal context
|
|
401
|
+
This stays private, sandboxed agents can't see it.
|
|
402
|
+
`);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Main
|
|
406
|
+
const command = process.argv[2];
|
|
407
|
+
|
|
408
|
+
switch (command) {
|
|
409
|
+
case 'setup':
|
|
410
|
+
case 'install':
|
|
411
|
+
interactiveSetup().catch(err => {
|
|
412
|
+
error(err.message);
|
|
413
|
+
process.exit(1);
|
|
414
|
+
});
|
|
415
|
+
break;
|
|
416
|
+
case 'verify':
|
|
417
|
+
case 'check':
|
|
418
|
+
verifyInteractive().catch(err => {
|
|
419
|
+
error(err.message);
|
|
420
|
+
process.exit(1);
|
|
421
|
+
});
|
|
422
|
+
break;
|
|
423
|
+
case 'help':
|
|
424
|
+
case '--help':
|
|
425
|
+
case '-h':
|
|
426
|
+
case undefined:
|
|
427
|
+
showHelp();
|
|
428
|
+
break;
|
|
429
|
+
default:
|
|
430
|
+
error(`Unknown command: ${command}`);
|
|
431
|
+
showHelp();
|
|
432
|
+
process.exit(1);
|
|
433
|
+
}
|