Kanban HTML 项目
Kanban 是由 HTML、CSS、JavaScript、图片和字体组成的静态项目。它直接显示配置产物目录中的 HTML 入口,不创建 conversation、run history、Workflow Output、Diagram 或 Logs,也不需要 executor。页面可以通过受限 JS Bridge 读写当前项目的 KV 和知识库,也可以按双方项目声明的权限读写关联项目 KV;页面本身不能选择项目或数据作用域。
最小目录
无参数项目只需要两个文件:
roadmap-kanban/
├── package.json
└── index.html
{
"id": "8bd4fef2-e083-4201-a89f-6f7d0676df53",
"name": "@workflow-code/roadmap-kanban",
"version": "0.1.0",
"private": true,
"type": "module",
"devDependencies": {
"@workflow-code/cli": "0.2.0",
"workflow-code": "0.2.0"
},
"workflowCode": {
"projectType": "kanban",
"projectInfo": {
"dataStorage": {
"mode": "both"
},
"relatedProjects": []
}
}
}
示例中的 Core 与 CLI 0.2.0 是当前精确兼容基线。新模板会写入当前 Core 与 CLI 版本;已有 Kanban 项目缺少这些依赖或 dataStorage 声明时,必须补齐并同步 lockfile,不能使用 ^、~、workspace:* 或其它 range。Kanban 页面不导入或执行 Core runtime,但服务器仍使用 devDependencies.workflow-code 判断宿主兼容性。
未配置入口时,产物目录默认为项目根目录 .,HTML 入口默认为 index.html,因此已有项目不需要迁移入口配置。Kanban 不应包含为运行而创建的 index.ts、interface.ts 或 workflow.defineExecutor(...)。没有 kanban.json 时,宿主隐藏全部配置控件,让 HTML 画布占满工作区。
Desktop 新建或克隆 Kanban 时会生成 UUID 并立即写入 package.json.id。手工导入的旧空 ID Kanban 仍可注册和编辑以完成迁移,但在 UUID 写回源码前不能访问项目数据或发布。创建并绑定云端应用时,Desktop 使用现有 package UUID;旧项目则使用其稳定注册 UUID 并写回 package.json.id,Server 不会另行分配。
构建产物与入口
Vue、React、Vite 或其它前端工程可以把浏览器静态产物放在独立目录,再由 Kanban 加载其中的 HTML 入口:
{
"workflowCode": {
"projectType": "kanban",
"projectInfo": {
"dataStorage": { "mode": "both" },
"relatedProjects": [
{
"alias": "stocks",
"projectId": "f4ee23d7-abbb-4ba3-8baf-75b7c6c0b964",
"grantToRelatedProject": {
"read": { "mode": "all" },
"write": { "mode": "none" }
}
}
]
},
"kanban": {
"artifactDir": "dist",
"entry": "index.html"
}
}
}
artifactDir相对项目根目录,默认"."。entry相对artifactDir,默认"index.html",支持.html和.htm。projectInfo.relatedProjects是所有项目类型共用的关联声明,不属于kanban配置。只有目标项目也声明当前项目时关系才确认,实际读写权限由目标项目的grantToRelatedProject决定。- 两个字段都禁止绝对路径、盘符、UNC、
..、目录逃逸和符号链接;.git、.hg、.svn与node_modules不能作为产物或入口目录。 - iframe 只读取
artifactDir内文件,不能通过相对路径访问源码、package.json或项目外文件。 - ES module、动态 import、WASM 和
fetch()可以读取产物目录内资源;CSP 的connect-src只允许当前短期预览 capability 对应的同源资源以及data:/blob:,不能访问公网接口。
Workflow Code Desktop 和 Server 不安装前端依赖、不自动运行构建脚本,也不连接 dev server、端口或 HMR。开发者使用自己的工具完成构建;在 Desktop 中使用小助手修改配置了 artifactDir / entry 的框架源码时,小助手可以在依赖已安装的前提下执行项目已有的一次性 build 并检查入口产物,但不会启动 dev/watch server 或擅自安装依赖。Desktop 只在产物目录内容、package.json 中的入口配置或 kanban.json 参数清单变化后刷新工作区,源码变化本身不会触发产物预览。Server Web 不监听本地目录;重新构建后需要通过 Desktop 或 CLI 再次上传。
纯 HTML、CSS、JavaScript、图片和字体通常可以跨操作系统使用。依赖原生模块、本机绝对路径、node_modules 或特定开发服务器的输出不属于受支持的静态浏览器产物。
参数清单
需要让同一个页面显示多组效果时,在根目录增加 kanban.json:
{
"version": 1,
"params": [
{
"name": "title",
"type": "string",
"label": "看板标题",
"defaultValue": "产品路线图"
},
{
"name": "accent",
"type": "string",
"label": "强调色",
"control": "radio",
"defaultValue": "#2563eb",
"options": [
{ "value": "#2563eb", "label": "蓝色" },
{ "value": "#0f766e", "label": "青色" }
]
},
{
"name": "columns",
"type": "string",
"label": "显示列",
"control": "checkboxes",
"multiple": true,
"options": [
{ "value": "todo", "label": "待处理" },
{ "value": "doing", "label": "进行中" },
{ "value": "done", "label": "已完成" }
]
}
]
}
version 当前必须为 1。params 直接复用 WorkflowParamDefinition:
| 能力 | 字段 |
|---|---|
| 文本、长文本 | type: "string",可选 control: "textarea" |
| 数字 | type: "number" |
| 开关 | type: "boolean" |
| 位置参数兼容 | type: "positional" |
| 文件 | type: "file",可选 accept、multiple、format: "json" |
| 单选枚举 | options 配合 control: "select" 或 "radio" |
| 多选枚举 | multiple: true 配合 control: "checkboxes" |
字段名在同一清单中必须唯一。参数默认值保存在 defaultValue 中;宿主会把 number、boolean 和多选值转换为 JSON 原生值后再注入页面。file 参数只注入平台文件引用的安全字段,不包含本地绝对路径或服务器存储路径。
kanban.json 适合描述同一页面的标题、主题、过滤条件、显示列和布局模式。任务、卡片、记录等页面业务数据应在 HTML 界面内提供新建、编辑和删除操作,再通过 KV Bridge 持久化。不要把业务列表编码成逗号分隔的 textarea 参数,让用户到配置区维护看板内容。
通过 Server 文件 API 保存图片、字体等二进制静态资源时,使用 encoding: "base64" 标记 content;读取、发布和源码下载会保留该编码和原始字节。文本文件不填写 encoding。
读取当前配置
宿主在页面加载前注入只读接口:
function render(configuration) {
const params = configuration?.params ?? {};
document.querySelector("h1").textContent = params.title || "看板";
document.documentElement.style.setProperty("--accent", params.accent || "#2563eb");
}
render(window.workflowCodeKanban.getConfiguration());
window.addEventListener("workflow-code:kanban-configuration-change", (event) => {
render(event.detail);
});
getConfiguration() 和事件的 detail 都返回:
{
id: string;
name: string;
params: Record<string, unknown>;
}
配置切换或参数变化不会重新加载 iframe。页面必须监听 workflow-code:kanban-configuration-change 并自行更新 DOM、canvas 或其它内部状态。持久参数只能在宿主配置区修改;页面内部业务数据应使用下述 KV 或知识库接口保存。
Desktop 的小助手可以通过 host_project_configuration 创建、读取、更新和删除宿主管理的命名配置。执行写操作前会先读取参数定义和当前配置;创建可指定名称和初始参数,更新可按配置 ID、名称或当前活动配置重命名并提交部分参数,删除必须明确指定 ID 或名称且不能删除最后一个配置。该工具按项目类型注册,只在小助手打开本地 Kanban 时加载;Workflow 和 Conversation 项目不会显示或启动它。工具只绑定当前目标 Kanban,不接受项目 ID、数据库路径或其它作用域;写入仍经过 Desktop Main 的参数校验和 SQLite 保存路径,并立即通知参数面板与 iframe。它不会把现有配置写回 kanban.json,也不能创建或伪造 file 参数引用。
KV 与知识库 Bridge
window.workflowCodeKanban 同时提供当前项目固定作用域的异步数据接口:
window.workflowCodeKanban = {
getConfiguration(): { id: string; name: string; params: Record<string, unknown> },
getRuntimeLocale(): Promise<"zh-CN" | "en">,
kv: {
getValue(key: string): Promise<unknown | undefined>,
setValue(key: string, value: JsonValue): Promise<void>,
},
relatedProjects: {
get(alias: string): {
kv: {
getValue(key: string): Promise<unknown | undefined>,
setValue(key: string, value: JsonValue): Promise<void>,
subscribe(
listener: (event: { alias: string; revision: string }) => void,
): () => void,
},
},
},
knowledge: {
listDocuments(options?: { pageSize?: number; cursor?: string }): Promise<KnowledgeDocumentPage>,
getDocument(id: string): Promise<KnowledgeDocument | null>,
createDocument(input: { id?: string; title: string; markdown: string }): Promise<KnowledgeDocument>,
editDocument(id: string, input: { title?: string; markdown?: string }): Promise<KnowledgeDocument>,
deleteDocument(id: string): Promise<{ id: string; deleted: boolean }>,
searchDocuments(options: {
query: string;
documentId?: string;
beforeLines?: number;
afterLines?: number;
maxMatches?: number;
pageSize?: number;
cursor?: string;
}): Promise<KnowledgeSearchResult>,
readDocumentLines(id: string, options: {
startLine: number;
endLine: number;
}): Promise<KnowledgeDocumentLinesResult>,
},
};
getRuntimeLocale() 异步返回当前 Desktop、Server Web 或 Embed 宿主选择的 "zh-CN" 或 "en"。明确支持国际化的 Kanban 应将页面文案、错误、ARIA 标签、日期和数字格式一并切换;旧宿主、Bridge 缺失或调用失败时必须回退到项目默认中文。kanban.json 仍是静态清单,不能按运行时语言动态替换。
KV key 由页面定义,宿主始终把当前项目 Bridge 限制在项目 scope。setValue 的 value 必须是有限数字、字符串、布尔值、null、数组或仅包含这些值的普通对象,不能传入 undefined、函数、循环引用或其它不可 JSON 序列化值。当前版本不提供 KV list 或 delete;需要覆盖状态时写入新值。
relatedProjects.get(alias).kv 只解析当前 Kanban 在 projectInfo.relatedProjects 中声明、且双方已确认的直接关系。getValue、setValue 分别检查目标项目对 Kanban 的 read/write 规则;目标通过 prefixes 限定 key 时,Bridge 逐次校验。iframe 请求只携带 alias、key 和写入 value,不能传入目标项目 UUID、scope、target 或数据库路径,也没有关联项目 list/delete 或 PersistentValue 接口。alias、UUID、数量和授权规则的完整限制见 KV 与会话。
relatedProjects.get(alias).kv.subscribe(listener) 用于感知目标项目 KV 的提交变化。订阅建立后,如果宿主已经取得当前 revision,会异步回调一次当前快照;后续每次回调只包含静态 alias 和不透明 revision,不说明具体 key。Server 不会把初始快照内同一 revision 再作为变化重复发送,并会把同一目标的连续写入合并到最新 revision。页面仍应把临近回调防抖合并为一次 getValue() 重读,并保留最后一次成功内容;自动刷新失败时显示非阻塞警告,不要清空正在展示的数据。返回函数用于取消订阅,页面卸载或配置切换时必须调用。
const unsubscribe = window.workflowCodeKanban.relatedProjects.get("stocks").kv.subscribe(
({ revision }) => {
scheduleRefresh(revision);
},
);
window.addEventListener("pagehide", unsubscribe, { once: true });
Desktop 要求关联项目已经导入本地项目注册表,并从该项目自己的 KV 目录读取;Desktop 会在活动看板 session 期间监听正式 value 文件。Server Web 预览和公开 Embed 只要求访问者能够读取当前 Kanban,不要求成为目标项目成员;双方当前声明和目标对 Kanban 的授权仍必须有效。关联写入还要求当前宿主允许该访问者管理 Kanban。鉴权 SSE 在连接和重连时先发送完整 revision 快照。目标不存在、关系待确认、授权不足、alias 无效或 key 越界都只返回安全的“关联项目不可用”错误;关系或读权限撤销、绑定失效时会关闭 revision 流。服务器按看板页面固定的 resolvedTarget 读取关系声明,但目标 KV 是项目级数据库,不随目标项目版本切换。
自动联动只发生在同一宿主后端:Desktop 本地 Workflow 更新本地 KV 后通知 Desktop 中已打开的 Kanban;Server 的手动运行、定时任务、Webhook 或 External API 更新 Server KV 后通知连接该 Server 的 Server Web 预览或 Embed。Desktop 与 Server 的 KV 不会相互自动同步。
下面的写法按命名配置隔离看板状态,并在没有 Bridge 的直接离线打开场景下降级为内存数据:
const configuration = window.workflowCodeKanban?.getConfiguration();
const storageKey = `kanban.board-state.v1:${configuration?.id || "default"}`;
const kv = window.workflowCodeKanban?.kv;
let board = { version: 1, tasks: [] };
if (kv) {
try {
board = (await kv.getValue(storageKey)) ?? board;
} catch {
showStorageError("加载失败,可重试");
}
}
async function saveBoard() {
if (!kv) return;
try {
await kv.setValue(storageKey, board);
showStorageStatus("已保存");
} catch {
showStorageError("保存失败,可重试");
}
}
每次调用返回 Promise。页面同一时间最多保留 16 个待处理请求;单次请求超过 15 秒会以 KANBAN_BRIDGE_TIMEOUT 拒绝。非 JSON KV 值或其它不可克隆输入会在页面侧以 KANBAN_BRIDGE_INVALID_REQUEST 拒绝,不会抛出浏览器原始 DataCloneError。失败 Error 只使用固定安全错误码和文案,不会包含数据库路径、堆栈或宿主内部信息。iframe 刷新后旧页面的迟到响应会被丢弃。
知识库列表每次最多读取 100 个文档;搜索的 pageSize / maxMatches 上限为 1000,beforeLines / afterLines 上限为 50;单次 readDocumentLines 最多读取 1000 行,且 endLine 不能小于 startLine。超出范围的调用会在进入存储层前被拒绝。
命名配置
只要 kanban.json.params 非空,宿主就会为当前用户维护命名配置:
- 第一次打开时创建“默认配置”。
- 新建配置使用参数默认值。
- 复制配置继承当前值。
- 修改参数后实时通知页面,并在约 300ms 防抖后保存。
- 可以重命名、选择或删除配置,但最后一个配置不能删除。
- 配置按项目和用户隔离,不写回源码,也不会在不同登录用户之间共享。
- Desktop 小助手可以在当前项目范围内读取或更新已有命名配置;
kanban.json仍只定义新配置的默认值。 kanban.json参数发生变化后,已有配置会补齐新增参数的默认值并移除废弃字段;Desktop 同时释放只被废弃 file 参数引用的本地文件。
桌面宽度使用左侧参数设置区和右侧 HTML 画布。设置区顶部的配置选择器负责切换配置,新建使用独立入口,重命名、复制和删除位于当前配置的操作菜单;参数控件在其下方全宽排列,保存状态固定显示在标题栏。低于 760px 的工作区容器使用顶部配置入口与参数抽屉,因此同一 Embed 即使位于较窄面板中也会自动切换布局。配置加载、保存和删除失败时保留当前值,并提供原位重试。
相对资源与安全边界
HTML 中的 CSS、JavaScript、图片和字体必须使用产物目录内相对路径:
<link rel="stylesheet" href="./assets/board.css" />
<img src="./assets/board.png" alt="看板预览" />
<script src="./assets/board.js"></script>
页面运行在 sandbox="allow-scripts" 中,并使用离线 Content Security Policy:
- 允许当前
artifactDir内资源以及必要的data:/blob:内容。 - 禁止公网请求、父页面 DOM、Node、Electron preload、弹窗、下载和顶层导航。
- 静态资源请求固定到当前 draft 或已发布版本;
..、绝对路径、符号链接逃逸、.env、项目元数据和缓存路径会被拒绝。 - KV、知识库与关联项目调用通过当前 iframe、随机 nonce、request id 和操作白名单校验;页面不能指定目标项目 UUID、scope、数据库路径或发布 target。
- 单个预览资源最大 20 MB。
不要依赖 CDN、远程字体、远程 API 或需要 allow-same-origin 的浏览器能力。页面内部可以自由响应点击、拖拽和修改 DOM;需要在刷新、重启或切换配置后恢复的业务数据,应显式写入 Bridge。
上传忽略规则
项目根目录的 .workflowignore 是 Desktop 和 CLI 打包上传文件时使用的唯一忽略配置。它独立于 Git,.gitignore 以及嵌套目录中的 .gitignore 都不参与上传判断;Server 只接收已经选择好的归档,Server Web 不读取本地目录。
规则支持注释、目录、通配符、** 和 ! 反向包含,路径统一按 / 匹配。缺少 .workflowignore、文件为空或只有注释时,会上传项目根目录下的全部普通文件,并在 Desktop 或 CLI 上传前显示文件数量和总大小警告。.workflowignore 自身始终保留在上传包中。
以下文件不能被排除:package.json、Workflow/Conversation 入口、Kanban 配置后的 HTML 入口,以及已存在的 kanban.json。如果规则忽略了必需文件,上传会指出命中的规则。符号链接、特殊设备文件、目录逃逸、单文件和包体大小限制属于独立安全校验,不会被忽略规则绕过。
推荐的 Kanban 配置:
.git/
node_modules/
coverage/
.cache/
.turbo/
*.log
*.tmp
*.tsbuildinfo
.env
.env.*
!.env.example
src/
tests/
dist/**/*.map
不要在这里忽略实际 artifactDir。例如 artifactDir 为 dist 时,可以让 .gitignore 排除 dist/ 以避免提交 Git,但 .workflowignore 必须保留 dist,这样构建产物仍会上传。
Desktop、Server Web 与 Embed
Desktop 可以新建默认 Kanban 或带参数示例,编辑并保存 HTML/CSS/JavaScript。根目录产物项目保存成功或外部文件变化后会刷新;配置独立 artifactDir 时,产物内容、入口配置或 kanban.json 参数清单变化会刷新,普通源码变化不会刷新。Monaco 中尚未保存的内容不会进入预览。项目详情提供“知识库”和“KV 数据”视图,显示与页面 Bridge 相同的本地项目数据。KV 与知识库分别写入当前项目隔离的本地 KV 和 PersistentValue 目录;本地项目重新绑定云端 UUID 后仍沿用同一目录命名空间。
Desktop 或 CLI 可以按 .workflowignore 选择本地 Kanban 的文本和二进制静态资源并上传到 Server。服务器不会为 Kanban 执行 tsup、安装依赖或生成 Node runtime,而是校验配置的 HTML 入口后保留静态产物;Server Web 只查看和管理已经上传的服务器版本。
Server Web 预览的当前项目 KV/知识库写入仅向拥有项目管理权限的用户开放,并固定当前项目与所选 target;关联项目访问按双方当前声明和目标项目对 Kanban 的读写授权判断,不要求访问者成为目标项目成员。公开 Embed 使用固定的已发布版本;访问 Kanban Embed 时必须同时具备有效 embed token 和登录用户身份,项目关系不会开放匿名访问。校验通过后,站点接口签发绑定项目、用户和固定版本的短期签名静态预览 capability,iframe 资源 URL 不再暴露长期 Embed token。签名使用 WORKFLOW_AUTH_COOKIE_SECRET,未配置时回退到 WORKFLOW_SERVER_ADMIN_KEY;多实例部署必须让各实例使用同一个稳定密钥,预览凭证才能跨实例和服务重启继续验证。Server Web 预览与 Embed 会在 capability 到期前自动换发,换发只重载 iframe 并保留当前命名配置;临时失败会重试,到期后仍无法换发时显示明确错误。页面进行 Bridge 或配置新建、复制、重命名、保存、切换、删除时都会携带首次加载得到的 resolvedTarget;如果 Embed token 已指向新发布版本,旧页面不会把新预览与旧 Bridge target 混用,而是提示刷新整个页面。命名配置按登录用户隔离,但项目 KV 和知识库由所有满足登录与 token 条件的访问者共享,访问者也可以修改或删除其中内容。预览资源 URL 带有短期 capability、CSP nonce 和固定版本,不能用于读取其它项目或版本。
项目关系与交付
workflowCode.projectInfo.relatedProjects 是所有项目类型共用的关系声明。Kanban 项目组只会从其中双方已确认的关系生成依赖锁;待确认关系可以随项目保存和发布,但不授予 KV 权限,也不会进入 included/external 依赖组。平台让发布者为每个已确认关系选择交付方式,并把结果固化到 Kanban 的精确已发布版本:
| 交付方式 | 版本依赖锁 | 下载行为 |
|---|---|---|
included | 固化关联项目的精确版本和 source hash;该版本必须保留源码。 | 有源码权限时随根项目下载;无权限时跳过并提供申请入口,不回退到关联项目 latest。 |
external | 只保留 alias 和关联项目 UUID,不锁定源码版本。 | 不随根项目下载,使用方自行准备关联项目。 |
项目组仍由多个独立项目组成,各自拥有 UUID、版本、成员和权限。Server 会先校验所有项目及关系并预留版本,再准备和发布 included 项目,最后激活 Kanban 根版本;中途失败不会让根版本上线。发布中断可以继续同一部署,已经成功的精确依赖不会重复发布。
源码交付与运行数据彼此独立:项目组归档、上传、发布和下载都拒绝携带 .env*、KV、定时计划、运行历史、SQLite 或宿主用户配置。Desktop 本地 KV 与 Server KV 也始终是两套数据库。部署后的 Server 数据需要开发者运行关联 Workflow entrypoint、调用 Server Run API 或使用自己的业务同步接口初始化;下载到本地后同样需要在本地重新运行同步。平台只维护调用和 Bridge API,不自动复制数据。
KV 权限完全来自双方 projectInfo,Server 不维护独立数据授权记录。目标项目可以向当前 Kanban 授予 none/all/prefixes 的读写能力;这不授予源码下载、运行或项目管理权限,撤销后新的 Bridge 访问和现有 revision 订阅立即失效。included 源码下载权限仍通过独立的 download_source 申请审批。Desktop 的“复制为本地项目”只有在所有 included 依赖都可下载时开放;复制会为整组生成新 UUID,重写所有被复制项目之间的 relatedProjects[].projectId,外部关系继续保留原 UUID。
发布、下载与 CLI
Kanban 发布始终保留静态源码,忽略 bundled 选择并使用 source 模式。图片、字体等二进制文件在文件 API 中以 encoding: "base64" 传输,CLI 和 Desktop 下载时会恢复原始字节。存在关联项目时,Desktop 发布对话框会显示本地项目、Server 项目、双方确认状态、源码权限和 Server 数据初始化状态;Server 缺少但本地存在的已确认项目默认建议作为 included 上传,发布者仍需明确确认。待确认项目会明确标记且不能勾选 included。
workflow-code structure ./roadmap-kanban
pnpm exec workflow-code workspace pack roadmap-kanban --path ./roadmap-kanban
pnpm exec workflow-code workspace upload roadmap-kanban --path ./roadmap-kanban --create
pnpm exec workflow-code workspace publish roadmap-kanban
pnpm exec workflow-code workspace download roadmap-kanban --target latest --path ./roadmap-kanban-copy
# 将 stocks 关联项目一起打包并发布;双方关系已写入各自 package.json
pnpm exec workflow-code workspace upload roadmap-kanban \
--path ./roadmap-kanban \
--dependency stocks=./stock-workflow \
--publish-group \
--release-log "发布行情看板及关联 Workflow"
workflow-code structure 会读取 projectType: "kanban"、artifactDir、entry 和可选参数清单。Kanban 是静态项目,workflow-code run、workflow-code json、远程 Run、debug node、Webhook 和 external run 都会返回明确的不支持执行错误。
使用生成器
python .agents/skills/workflow-code-generator/scripts/create_workflow.py roadmap \
--dir ./roadmap-kanban --project-type kanban
python .agents/skills/workflow-code-generator/scripts/create_workflow.py roadmap \
--dir ./roadmap-kanban --project-type kanban --with-kanban-params
两条命令都会生成完整的可编辑看板工程,包括 .workflowignore、package.json、index.html、board-state.js、board-storage.js、README 和纯逻辑测试。--with-kanban-params 额外生成只包含标题和强调色的 kanban.json;任务始终在页面内新建、编辑、拖拽和删除,并通过 KV Bridge 按配置隔离保存。
生成器默认创建简体中文项目,不会自动加入翻译资源、语言切换或 locale 检测。只有明确要求国际化或多语言支持,并说明目标语言后,才会为项目加入对应适配。