feat(page-data): add private and public dataset API (phase 1-3)
Extract UserDataSpaceService for shared SQLite access, wire logged-in Page Data routes, and add public insert plus password-token read/update/delete with policy storage, rate limits, and regression tests. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -129,6 +129,66 @@ test('sandbox MCP exposes and protects the user private data space', async (t) =
|
||||
assert.match(dotCommand.result.content[0].text, /dot command/);
|
||||
});
|
||||
|
||||
test('sandbox MCP registers datasets and page policies for Page Data API', async (t) => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mindspace-sandbox-page-data-'));
|
||||
const server = startSandbox(root, {
|
||||
ALLOWED_TOOLS: 'private_data_execute,private_data_register_dataset,private_data_set_page_policy',
|
||||
PRIVATE_DATA_USER_ID: 'user-1',
|
||||
});
|
||||
t.after(() => server.child.kill());
|
||||
|
||||
await server.request('initialize');
|
||||
|
||||
const create = await server.request('tools/call', {
|
||||
name: 'private_data_execute',
|
||||
arguments: {
|
||||
sql: `CREATE TABLE signups (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
phone TEXT
|
||||
);`,
|
||||
},
|
||||
});
|
||||
assert.equal(create.result.isError, false);
|
||||
|
||||
const register = await server.request('tools/call', {
|
||||
name: 'private_data_register_dataset',
|
||||
arguments: {
|
||||
name: 'signups',
|
||||
table: 'signups',
|
||||
actions: ['read', 'insert'],
|
||||
columns: {
|
||||
read: ['id', 'name', 'phone'],
|
||||
insert: ['name', 'phone'],
|
||||
},
|
||||
},
|
||||
});
|
||||
assert.equal(register.result.isError, false);
|
||||
assert.match(register.result.content[0].text, /"name": "signups"/);
|
||||
|
||||
const policy = await server.request('tools/call', {
|
||||
name: 'private_data_set_page_policy',
|
||||
arguments: {
|
||||
pageId: 'page-1',
|
||||
accessMode: 'public',
|
||||
datasets: {
|
||||
signups: {
|
||||
insert: true,
|
||||
columns: { insert: ['name', 'phone'] },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
assert.equal(policy.result.isError, false);
|
||||
assert.equal(
|
||||
fs.existsSync(path.join(root, '.mindspace', 'page-data-policies', 'page-1.json')),
|
||||
true,
|
||||
);
|
||||
const saved = JSON.parse(fs.readFileSync(path.join(root, '.mindspace', 'page-data-policies', 'page-1.json'), 'utf8'));
|
||||
assert.equal(saved.pageId, 'page-1');
|
||||
assert.equal(saved.datasets.signups.insert, true);
|
||||
});
|
||||
|
||||
test('sandbox MCP exposes schedule tools only when schedule env is configured', async (t) => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mindspace-sandbox-schedule-'));
|
||||
const server = startSandbox(root, {
|
||||
|
||||
Reference in New Issue
Block a user