7.5 KiB
7.5 KiB
MindSpace Service Contract
日期: 2026-07-02
状态: Draft for P1/P2
生产拓扑更新(2026-07-03):
- 103 的 MindSpace Service 已独立部署在
/Users/john/MindSpace,服务为cn.tkmind.mindspace-service,端口8082。 - Portal live 目录仍是
/Users/john/Project/Memind,但它不再是 MindSpace Service 的根目录。 /Users/john/Project/Memind/MindSpace只能当旧链路兼容/存量目录处理;新排障和新配置必须优先看 103 runtime topology。
目标:
- 定义 MindSpace 从 Memind Portal 单体拆出前必须稳定下来的 API、存储、URL、权限和 package 契约。
- 允许先在单体内实现,再独立成进程。
- 保证 Memind App 和 Goose Runtime 不依赖 MindSpace 的物理部署位置。
1. 边界原则
MindSpace Service 是以下对象的唯一权威:
- Space
- Category
- Asset
- Asset Version
- Page
- Page Version
- Publication
- Conversation Package
- Package Manifest
- Canonical Public URL
- Storage Key 到 backing object 的校验
Memind App 只调用 API,不直接读写 MindSpace/ 或 data/mindspace/。
Goose Runtime 只通过 MindSpace API/MCP 获得 scoped workspace capability,不直接拥有 MindSpace 业务状态。
2. API Surface
建议从当前 /api/mindspace/v1/* 延续,先在单体内实现兼容 facade:
GET /api/mindspace/v1/spaces/current
GET /api/mindspace/v1/assets
POST /api/mindspace/v1/assets/uploads
GET /api/mindspace/v1/assets/:assetId/download
GET /api/mindspace/v1/pages
POST /api/mindspace/v1/pages
GET /api/mindspace/v1/pages/:pageId
POST /api/mindspace/v1/pages/:pageId/publish
GET /api/mindspace/v1/publications/:publicationId
GET /api/mindspace/v1/conversation-packages
POST /api/mindspace/v1/conversation-packages/ensure
GET /api/mindspace/v1/conversation-packages/:packageId
GET /api/mindspace/v1/conversation-packages/:packageId/manifest
POST /api/mindspace/v1/conversation-packages/:packageId/artifacts
所有响应必须包含稳定 ID,不把物理路径作为前端契约。
3. Storage Adapter
MindSpace Service 内部存储接口:
interface MindSpaceStorageAdapter {
putObject(key, body, options)
getObject(key)
statObject(key)
deleteObject(key)
listObjects(prefix, options)
copyObject(sourceKey, targetKey, options)
createReadStream(key)
createWriteStream(key, options)
getSignedUrl(key, options)
}
Adapter 要求:
key是相对 storage root 的逻辑 object key,禁止绝对路径。- local fs adapter 可以把 key 映射到磁盘。
- NAS adapter 可以把 key 映射到共享挂载。
- S3 adapter 可以把 key 映射到 object prefix。
- 业务层不得依赖 adapter 的物理实现。
4. Canonical URL
URL 生成入口:
createPublicPageUrl({ userId, publicationId, pageId, slug })
createPublicAssetUrl({ userId, assetId, variant })
canonicalizeMindSpaceUrl(inputUrl)
validatePublicBackingObject({ publicationId })
规则:
- 新代码不得直接拼
/MindSpace/<userId>/...。 - 当前分支新增的
mindspace-canonical-url.mjs是 P1 facade 起点;后续旧 helper 迁移到它后,再接入 backing object 校验。 - 当前分支新增的
mindspace-service.mjs是组合门面起点;它先组合 package manifest、storage adapter、canonical URL,不接入现有运行链路。 - 旧 URL helper 暂时保留为 compatibility layer。
- public URL 必须能反查到 publication 或 asset。
- URL 返回前应校验 backing object 存在,或明确返回 pending 状态。
5. Conversation Package
Conversation package 是“每次对话文件夹包”的业务抽象。
逻辑 URI:
mindspace://users/<userId>/conversations/<sessionId>
推荐 manifest:
{
"schemaVersion": 1,
"packageId": "cp_xxx",
"userId": "u_xxx",
"sessionId": "s_xxx",
"title": "对话标题",
"storagePrefix": "users/u_xxx/conversations/s_xxx",
"artifacts": [
{
"artifactId": "ca_xxx",
"kind": "public_html",
"assetId": "asset_xxx",
"pageId": "page_xxx",
"publicationId": "pub_xxx",
"messageId": "msg_xxx",
"agentRunId": "run_xxx",
"displayName": "report.html",
"mimeType": "text/html",
"sizeBytes": 12345,
"storageKey": "users/u_xxx/conversations/s_xxx/public/report.html",
"canonicalUrl": "https://...",
"createdAt": 1783000000000
}
]
}
Artifact kind:
input_image
input_file
generated_image
generated_file
page
public_html
long_image
thumbnail
docx
pdf
6. Metadata Tables
首选新增:
CREATE TABLE h5_conversation_packages (
id VARCHAR(64) PRIMARY KEY,
user_id CHAR(36) NOT NULL,
session_id VARCHAR(128) NOT NULL,
title VARCHAR(255) DEFAULT NULL,
status ENUM('active', 'archived', 'deleted') NOT NULL DEFAULT 'active',
storage_prefix VARCHAR(512) DEFAULT NULL,
manifest_asset_id CHAR(36) DEFAULT NULL,
created_at BIGINT NOT NULL,
updated_at BIGINT NOT NULL,
UNIQUE KEY uniq_conversation_package_session (user_id, session_id)
);
CREATE TABLE h5_conversation_artifacts (
id VARCHAR(64) PRIMARY KEY,
package_id VARCHAR(64) NOT NULL,
asset_id CHAR(36) DEFAULT NULL,
page_id CHAR(36) DEFAULT NULL,
publication_id CHAR(36) DEFAULT NULL,
agent_run_id CHAR(36) DEFAULT NULL,
message_id VARCHAR(128) DEFAULT NULL,
role VARCHAR(32) DEFAULT NULL,
artifact_kind VARCHAR(64) NOT NULL,
display_name VARCHAR(255) DEFAULT NULL,
storage_key VARCHAR(512) DEFAULT NULL,
canonical_url VARCHAR(512) DEFAULT NULL,
sort_order INT NOT NULL DEFAULT 0,
created_at BIGINT NOT NULL,
KEY idx_conversation_artifacts_package (package_id),
KEY idx_conversation_artifacts_asset (asset_id),
KEY idx_conversation_artifacts_page (page_id),
KEY idx_conversation_artifacts_message (message_id)
);
迁移原则:
- 先新增表,不改旧表语义。
- 新写入链路双写 artifact 归属。
- 历史数据 best-effort 回填。
- 回填失败不影响旧页面访问。
7. Goose Capability
短期兼容:
{
"sandboxRoot": "/absolute/local/MindSpace/<userId>",
"serverPath": "/absolute/local/mindspace-sandbox-mcp.mjs"
}
长期目标:
{
"workspaceRef": "mindspace://users/<userId>/conversations/<sessionId>",
"packageId": "cp_xxx",
"apiBaseUrl": "https://mindspace.example.com",
"accessToken": "short-lived-scoped-token",
"allowedTools": ["read_file", "write_file", "edit_file", "publish_page"]
}
权限要求:
- Token 必须绑定 user、session、package。
- Token 必须有过期时间。
- Tool allowlist 必须由 Memind/MindSpace policy 共同决定。
- Goose 不得凭 token 访问其它用户 package。
8. Acceptance Tests
P1:
- local fs adapter 拒绝绝对 key 和
..traversal。 - canonical URL 只通过 facade 生成。
- 旧
MindSpace/<userId>/public/*.html链路不变。 npm run verify:mindspace-publish-guards通过。
P2:
- 上传图片后 package manifest 出现
input_image。 - 生成 HTML 后 package manifest 出现
public_html。 - 发布页面后 artifact 关联
pageId和publicationId。 - 同一 session 多次生成按
sortOrder或createdAt稳定排序。
P3:
- MindSpace Service 停止时,Memind App 明确返回服务不可用。
- 替换 local storage root 不需要改 Memind App / Goose 业务逻辑。
9. Non-Goals
当前阶段不做:
- 不迁移生产 103。
- 不切换 NAS/S3。
- 不删除旧 URL helper。
- 不改变现有
/MindSpace/*公开访问行为。 - 不改变 Goose 当前可用的
sandboxRoot兼容路径。