feat(mindspace): enforce PostgreSQL user data delivery

This commit is contained in:
john
2026-07-13 15:29:29 +08:00
parent b33c943b69
commit a6620fb719
57 changed files with 2779 additions and 164 deletions
+10 -3
View File
@@ -24,6 +24,7 @@ import { closePolicyDataset, normalizePageAccessPolicy } from './page-access-pol
import { upsertPageDataPolicyIndex } from './page-data-policy-index.mjs';
import { bindWorkspaceHtmlForPageData } from './page-data-workspace-bind.mjs';
import { resolveMindSpaceStorageRoot } from './mindspace-runtime-config.mjs';
import { assertNoProhibitedBrowserStorage } from './mindspace-browser-storage-policy.mjs';
const SANDBOX_ROOT = process.argv[2]?.trim() || process.env.SANDBOX_ROOT?.trim();
if (!SANDBOX_ROOT) {
@@ -215,7 +216,7 @@ const ALL_TOOLS = [
{
name: 'private_data_info',
description:
'查看当前用户“用户私有数据空间”的状态。每个用户只有一个私有 SQLite 数据库,适合保存问卷、表单、清单、调研数据和分析中间表;不要创建额外 SQLite 文件。',
'查看当前用户“用户私有数据空间”的状态。每个用户只有一个由平台分配和隔离的数据空间,适合保存问卷、表单、清单、调研数据和分析中间表;不要创建额外数据库文件。',
inputSchema: { type: 'object', properties: {} },
},
{
@@ -504,6 +505,7 @@ async function callTool(name, args) {
}
case 'write_file': {
const abs = resolveSandboxed(args.path);
assertNoProhibitedBrowserStorage(args.content ?? '', { relativePath: args.path });
fs.mkdirSync(path.dirname(abs), { recursive: true });
fs.writeFileSync(abs, args.content ?? '', 'utf8');
return [{ type: 'text', text: `已写入 ${args.path}${(args.content ?? '').length} 字节)` }];
@@ -516,6 +518,7 @@ async function callTool(name, args) {
throw new Error('edit_file: old_str 在文件中不存在,替换失败');
}
const updated = oldStr ? original.replace(oldStr, args.new_str ?? '') : (args.new_str ?? '');
assertNoProhibitedBrowserStorage(updated, { relativePath: args.path });
fs.writeFileSync(abs, updated, 'utf8');
return [{ type: 'text', text: `已编辑 ${args.path}` }];
}
@@ -578,7 +581,9 @@ async function callTool(name, args) {
{
...info,
rules: [
'每个用户只能使用这个唯一 SQLite 数据库',
info.backend === 'postgres'
? '每个用户只能使用新 PG 中分配给自己的独立 schema'
: '每个用户只能使用这个唯一 SQLite 数据库',
'适合问卷、表单、清单、调研数据和分析中间表',
'不要存账号、计费、权限、审计、公开平台数据或跨用户数据',
'HTML 页面通过 Page Data API 访问 dataset,不要直接暴露 SQL',
@@ -603,7 +608,9 @@ async function callTool(name, args) {
return [
{
type: 'text',
text: `已执行。当前数据空间大小 ${result.sizeBytes} 字节${result.quotaSynced ? `,已同步空间占用 ${result.deltaBytes} 字节` : ''}`,
text: result.backend === 'postgres'
? `已在用户专属 PG schema 中执行。命令 ${result.command ?? 'OK'},影响 ${result.affectedRows ?? 0}`
: `已执行。当前数据空间大小 ${result.sizeBytes} 字节${result.quotaSynced ? `,已同步空间占用 ${result.deltaBytes} 字节` : ''}`,
},
];
}