@nestos/kanban 0.5.1 → 0.5.2
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/LICENSE +674 -0
- package/README.md +73 -29
- package/dist/{activity-C6xDM-cg.d.cts → activity-BbC4sgKL.d.cts} +28 -2
- package/dist/{activity-Dw8DOw0L.d.ts → activity-Czlan5xW.d.ts} +28 -2
- package/dist/activity-module-DaO1Rry0.d.cts +22 -0
- package/dist/activity-module-T22wliZu.d.ts +22 -0
- package/dist/activity-ports-Dqxa86zT.d.cts +12 -0
- package/dist/activity-ports-cxfrelaL.d.ts +12 -0
- package/dist/activity.cjs +10 -282
- package/dist/activity.cjs.map +1 -1
- package/dist/activity.d.cts +8 -30
- package/dist/activity.d.ts +8 -30
- package/dist/activity.js +9 -281
- package/dist/activity.js.map +1 -1
- package/dist/{chunk-O4OGAAV4.js → chunk-3JYLJPE5.js} +29 -5
- package/dist/chunk-3JYLJPE5.js.map +1 -0
- package/dist/{chunk-ATUFFNLV.js → chunk-3LEIHB7Y.js} +5 -5
- package/dist/chunk-4XF6LQSU.cjs.map +1 -1
- package/dist/chunk-5Q7WB63A.cjs +282 -0
- package/dist/chunk-5Q7WB63A.cjs.map +1 -0
- package/dist/{chunk-PSKQAFDF.cjs → chunk-7KDOTBRX.cjs} +1 -1
- package/dist/chunk-7KDOTBRX.cjs.map +1 -0
- package/dist/{chunk-5DLZWW5Y.js → chunk-AHHEGTUA.js} +3 -3
- package/dist/chunk-AOHQCX7K.cjs +95 -0
- package/dist/chunk-AOHQCX7K.cjs.map +1 -0
- package/dist/{chunk-IUI5CI2S.cjs → chunk-DINLR6E6.cjs} +96 -92
- package/dist/chunk-DINLR6E6.cjs.map +1 -0
- package/dist/{chunk-L44TM4EE.cjs → chunk-EYTVC2H5.cjs} +568 -454
- package/dist/chunk-EYTVC2H5.cjs.map +1 -0
- package/dist/chunk-FEE2YBZ7.js +1972 -0
- package/dist/chunk-FEE2YBZ7.js.map +1 -0
- package/dist/chunk-FNO5K3A7.js +282 -0
- package/dist/chunk-FNO5K3A7.js.map +1 -0
- package/dist/chunk-IHHDXZPZ.js +382 -0
- package/dist/chunk-IHHDXZPZ.js.map +1 -0
- package/dist/chunk-J3EAIVA3.js +280 -0
- package/dist/chunk-J3EAIVA3.js.map +1 -0
- package/dist/{chunk-DFTOTBGF.js → chunk-KH3WF3K4.js} +20 -16
- package/dist/chunk-KH3WF3K4.js.map +1 -0
- package/dist/{chunk-TB2RZGDV.js → chunk-M35X5PHO.js} +14 -1
- package/dist/chunk-M35X5PHO.js.map +1 -0
- package/dist/chunk-QRGZD2JU.cjs +1972 -0
- package/dist/chunk-QRGZD2JU.cjs.map +1 -0
- package/dist/{chunk-4KF7KUAI.cjs → chunk-RBOER6EW.cjs} +6 -6
- package/dist/chunk-RBOER6EW.cjs.map +1 -0
- package/dist/{chunk-6MN3H3YF.cjs → chunk-RSH6QSEC.cjs} +16 -3
- package/dist/chunk-RSH6QSEC.cjs.map +1 -0
- package/dist/{chunk-T7P5EIC4.cjs → chunk-VMRDXOLX.cjs} +33 -33
- package/dist/chunk-VMRDXOLX.cjs.map +1 -0
- package/dist/{chunk-5XKOCKY2.js → chunk-X4UHZX6F.js} +1 -1
- package/dist/chunk-XBSGUVCY.cjs +382 -0
- package/dist/chunk-XBSGUVCY.cjs.map +1 -0
- package/dist/{chunk-OHQTKGQN.js → chunk-XKDZHAUE.js} +242 -128
- package/dist/chunk-XKDZHAUE.js.map +1 -0
- package/dist/chunk-ZUZGAO6G.cjs +280 -0
- package/dist/chunk-ZUZGAO6G.cjs.map +1 -0
- package/dist/{content-CAIp1njn.d.cts → content-C5aJZFlw.d.cts} +1 -1
- package/dist/{content-CGMJdyH_.d.ts → content-hLH5BNud.d.ts} +1 -1
- package/dist/contracts/index.cjs +13 -3
- package/dist/contracts/index.cjs.map +1 -1
- package/dist/contracts/index.d.cts +5 -4
- package/dist/contracts/index.d.ts +5 -4
- package/dist/contracts/index.js +14 -4
- package/dist/{discovery-DxpirOz3.d.cts → discovery-9E-LfwIw.d.cts} +1 -1
- package/dist/{discovery-gSqWpuRm.d.ts → discovery-D60yrjej.d.ts} +1 -1
- package/dist/discovery.cjs +63 -65
- package/dist/discovery.cjs.map +1 -1
- package/dist/discovery.d.cts +4 -4
- package/dist/discovery.d.ts +4 -4
- package/dist/discovery.js +5 -7
- package/dist/discovery.js.map +1 -1
- package/dist/execution-bridge-fpCnH6z5.d.cts +52 -0
- package/dist/execution-bridge-rk0uVy_n.d.ts +52 -0
- package/dist/execution.cjs +6 -356
- package/dist/execution.cjs.map +1 -1
- package/dist/execution.d.cts +4 -51
- package/dist/execution.d.ts +4 -51
- package/dist/execution.js +9 -359
- package/dist/execution.js.map +1 -1
- package/dist/index.cjs +10 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -16
- package/dist/index.d.ts +9 -16
- package/dist/index.js +10 -12
- package/dist/index.js.map +1 -1
- package/dist/{module-CWNK2BMj.d.ts → module-CYfE7YhM.d.ts} +2 -2
- package/dist/{module-BbvwdCDF.d.cts → module-Ca9L3qCn.d.cts} +2 -2
- package/dist/{planning-CcFZYdIL.d.cts → planning-CHrOqQ6b.d.cts} +45 -10
- package/dist/{planning-CcFZYdIL.d.ts → planning-CHrOqQ6b.d.ts} +45 -10
- package/dist/{ports-Bn6ro2FB.d.ts → ports-B0McVg7B.d.ts} +4 -3
- package/dist/ports-BeGO1VRI.d.cts +49 -0
- package/dist/{ports-CaL6geqR.d.cts → ports-GQy0x-RI.d.cts} +4 -3
- package/dist/ports-ykGiNUSx.d.ts +49 -0
- package/dist/runtime/index.cjs +205 -0
- package/dist/runtime/index.cjs.map +1 -0
- package/dist/runtime/index.d.cts +75 -0
- package/dist/runtime/index.d.ts +75 -0
- package/dist/runtime/index.js +205 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/integration.cjs +423 -0
- package/dist/runtime/integration.cjs.map +1 -0
- package/dist/runtime/integration.d.cts +658 -0
- package/dist/runtime/integration.d.ts +658 -0
- package/dist/runtime/integration.js +423 -0
- package/dist/runtime/integration.js.map +1 -0
- package/dist/storage.cjs +9 -277
- package/dist/storage.cjs.map +1 -1
- package/dist/storage.d.cts +6 -6
- package/dist/storage.d.ts +6 -6
- package/dist/storage.js +11 -279
- package/dist/storage.js.map +1 -1
- package/dist/{store-q63eSMoT.d.cts → store-DPwHzGiz.d.cts} +2 -2
- package/dist/{store-Ct0gOAAg.d.ts → store-DgdrPCV9.d.ts} +2 -2
- package/dist/task-projection-CSorlMws.d.ts +11 -0
- package/dist/task-projection-DKKB_ueb.d.cts +11 -0
- package/examples/standalone.ts +22 -0
- package/package.json +117 -87
- package/dist/chunk-2YFPV5GF.cjs +0 -16
- package/dist/chunk-2YFPV5GF.cjs.map +0 -1
- package/dist/chunk-4KF7KUAI.cjs.map +0 -1
- package/dist/chunk-6MN3H3YF.cjs.map +0 -1
- package/dist/chunk-DFTOTBGF.js.map +0 -1
- package/dist/chunk-G6FCDLDF.js +0 -16
- package/dist/chunk-G6FCDLDF.js.map +0 -1
- package/dist/chunk-IUI5CI2S.cjs.map +0 -1
- package/dist/chunk-KYNYPKKH.cjs +0 -71
- package/dist/chunk-KYNYPKKH.cjs.map +0 -1
- package/dist/chunk-L44TM4EE.cjs.map +0 -1
- package/dist/chunk-LSE6JG5I.cjs +0 -20
- package/dist/chunk-LSE6JG5I.cjs.map +0 -1
- package/dist/chunk-O4OGAAV4.js.map +0 -1
- package/dist/chunk-OHQTKGQN.js.map +0 -1
- package/dist/chunk-PSKQAFDF.cjs.map +0 -1
- package/dist/chunk-QTMTBEBA.js +0 -20
- package/dist/chunk-QTMTBEBA.js.map +0 -1
- package/dist/chunk-T7P5EIC4.cjs.map +0 -1
- package/dist/chunk-TB2RZGDV.js.map +0 -1
- package/dist/ui/index.cjs +0 -1961
- package/dist/ui/index.cjs.map +0 -1
- package/dist/ui/index.d.cts +0 -209
- package/dist/ui/index.d.ts +0 -209
- package/dist/ui/index.js +0 -1961
- package/dist/ui/index.js.map +0 -1
- package/dist/ui/kanban.css +0 -194
- /package/dist/{chunk-ATUFFNLV.js.map → chunk-3LEIHB7Y.js.map} +0 -0
- /package/dist/{chunk-5DLZWW5Y.js.map → chunk-AHHEGTUA.js.map} +0 -0
- /package/dist/{chunk-5XKOCKY2.js.map → chunk-X4UHZX6F.js.map} +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
# @nestos/kanban
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## 独立安装与开发
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npm install @nestos/kanban @nestos/vfs-core
|
|
7
|
+
# 在本仓库开发
|
|
8
|
+
pnpm install --frozen-lockfile
|
|
9
|
+
pnpm typecheck
|
|
10
|
+
pnpm test
|
|
11
|
+
pnpm build
|
|
12
|
+
pnpm pack
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
本包提供领域服务与运行时,不包含 DOM 界面、CSS 或 UI 依赖。`/contracts` 为前台与远程调用提供无 I/O 的契约和纯函数。
|
|
16
|
+
|
|
17
|
+
基于 VFS 的通用看板与工作协调库,存储与运行时通过可选 peer `@nestos/vfs-core` 接入 VFS;使用这些入口时显式安装该依赖。提供唯一责任人、估算与在制额度、依赖、验收、归档、日志和决策,以及持久派发意图。库不调用模型、不创建 Session/Task、不安装后台调度器。执行、来源与资源活动通过可选端口接入。
|
|
4
18
|
|
|
5
19
|
```ts
|
|
6
20
|
import {SeqKanbanStore, createKanbanModule} from '@nestos/kanban';
|
|
@@ -26,39 +40,17 @@ const service = createKanbanModule({
|
|
|
26
40
|
| `/storage` | `SeqKanbanStore`、`ConditionalKanbanStore`、内容安装与传输端口 |
|
|
27
41
|
| `/execution` | `createKanbanExecutionBridge`;宿主提供 prepare/start/inspect |
|
|
28
42
|
| `/discovery` | `createKanbanDiscovery`;宿主提供来源、分析和授权 |
|
|
43
|
+
| `/runtime` | `createKanbanRuntime`;单一工厂、执行意图/句柄/回执及完整协调 |
|
|
44
|
+
| `/integration` | 自定义宿主的装配、业务协调与语义状态接口 |
|
|
29
45
|
| `/activity` | `createKanbanActivityModule`;注入来源和外部引用 namespace |
|
|
30
|
-
| `/ui`、`/ui.css` | DOM 工作区、`mountKanban` 与独立样式 |
|
|
31
46
|
|
|
32
47
|
`createKanban(options)` 提供 `boards.create/get/list/overview`、`items.get/query`、`records.get/query`、`planning.get`、`references.resolve/suggest`、`execute`、`events`、`tick` 和 `quiesce/dispose`。execute 返回 `{boardId, operationId, revision}`;幂等重放时 revision 是当前返回快照的版本。需要 KanbanService(例如 UI、执行桥)时直接使用 `createKanbanModule`,二者共享同一实现与命令协议。
|
|
33
48
|
|
|
34
49
|
主体 ID、Session/Task/Message ID 及历史持久格式保留。独立 Agent 可以只提供自己的 ID;若提供执行身份,所有权继续按该身份隔离。NestOS 在适配边界继续要求真实 Session/Task,宿主始终负责实际权限核验。
|
|
35
50
|
|
|
36
|
-
##
|
|
37
|
-
|
|
38
|
-
```ts
|
|
39
|
-
import {mountKanban} from '@nestos/kanban/ui';
|
|
40
|
-
import '@nestos/kanban/ui.css';
|
|
41
|
-
|
|
42
|
-
const view = await mountKanban(container, {
|
|
43
|
-
service,
|
|
44
|
-
presentation: {
|
|
45
|
-
text: (key, values) => translations(key, values),
|
|
46
|
-
icons: {refreshView, more, close},
|
|
47
|
-
log: logMetadata,
|
|
48
|
-
referenceUrl: path => resourceUrl(path),
|
|
49
|
-
},
|
|
50
|
-
selected: path => saveNavigation(path),
|
|
51
|
-
openSession: id => openResource(id),
|
|
52
|
-
});
|
|
53
|
-
await view.open({boardId: 'work'});
|
|
54
|
-
await view.dispose();
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
文案 key 不带 `kanban.` 前缀;图标为可信宿主提供的 SVG/HTML。主题通过 `--kanban-*` CSS 变量设置;NestOS 在适配样式中映射原有主题变量。所有呈现配置按实例注入,不存在全局语言、当前宿主或跨实例回调。UI 不导入 NestOS、common 或 vfs-ui。资源活动为可选能力;未接入时不显示执行筛选,也不安装活动轮询。接入时可见且没有未保存输入才轮询,销毁后停止;通知不会覆盖草稿。错误只展示安全错误码与宿主文案,不输出正文或完整请求。
|
|
51
|
+
## 前台界面
|
|
58
52
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
执行桥不解释模型或业务。执行桥和来源扫描的 `tick()`(兼容 `scan()`)由宿主显式驱动;扫描 `quiesce/dispose` 拒绝新准入并排空已接纳操作。只有注入 subscribe 才接收唤醒。executionReference 将实际执行 ID 映射为宿主命名空间,不在核心写死 NestOS 引用。`ConditionalKanbanStore` 只要求 query/transaction 与可选正文资产端口,适配器负责授权连接及 unknown 写入核对,不在 Store 内盲重试。
|
|
53
|
+
界面由独立包 `@nestos/kanban-ui` 提供,样式入口为 `@nestos/kanban-ui/style.css`。前台仅通过本库 `/contracts` 中的 KanbanClient 接入本地或远程服务,不依赖后台存储、执行或恢复实现。
|
|
62
54
|
|
|
63
55
|
## 版本化内容安装
|
|
64
56
|
|
|
@@ -109,7 +101,7 @@ VFS `seq:committed` 可用于唤醒宿主,但持久状态与定期补扫才是
|
|
|
109
101
|
|
|
110
102
|
关闭顺序:停止事件/定时准入 → service.quiesce(拒新操作、等待已准入事务)→ 排空执行适配器 → 最后关闭 VFS。宿主重启后扫描持久派发、检查原执行身份并补投 outbox。SeqFile 不能与模型调用、远程服务或另一数据库组成单一事务,执行桥必须保留提交前日志和 unknown 状态。
|
|
111
103
|
|
|
112
|
-
NestOS 的默认目录、远程 HttpSeqClient、Session 来源读取、发布 Agent/Flow、Kernel 回执、系统 notify 与调度接线均位于 `backend/app-core/src/kanban/`。扫描状态机、批量提交、远程事务编码与执行恢复归独立库;UI 的 NestOS 包装位于 `ui/app-shell/src/kanban/`。应用入口只导出实际 NestOS 绑定,不代转出库 API,核心没有这些运行依赖。完整规范见 [Kanban 协调设计](
|
|
104
|
+
NestOS 的默认目录、远程 HttpSeqClient、Session 来源读取、发布 Agent/Flow、Kernel 回执、系统 notify 与调度接线均位于 `backend/app-core/src/kanban/`。扫描状态机、批量提交、远程事务编码与执行恢复归独立库;UI 的 NestOS 包装位于 `ui/app-shell/src/kanban/`。应用入口只导出实际 NestOS 绑定,不代转出库 API,核心没有这些运行依赖。完整规范见 [Kanban 协调设计](https://github.com/nest-agentos/NestOS/blob/main/doc/design/kanban-coordination.md)。
|
|
113
105
|
|
|
114
106
|
会话活动入口 `executionSession(boardId, itemId)` 只读取当前执行摘要。显式传入 `{includeRelated: true}` 时,通过可选 `KanbanSessionSource.related` 合并宿主关联与已有执行绑定、参与者;返回的 `sessions` 按不透明 Session ID 去重,各自包含用途和 `executions` 执行记录。续办在同一 Session 下保留不同尝试,`current` 仅由正式执行绑定决定。UI 在展开「相关会话」时加载,支持刷新和宿主注入的原始资源链接;普通活动轮询不读取历史。资源属性由宿主提供,库不识别工作区、Git 或原生会话协议。
|
|
115
107
|
|
|
@@ -119,4 +111,56 @@ Boards may store automation settings with opaque planner/executor references and
|
|
|
119
111
|
|
|
120
112
|
宿主已有工作区导航时,可直接构造 `KanbanWorkbench` 并省略 `sidebar`,通过 `createBoard()` 接入导航动作,通过 `committed` 刷新目录;`mountKanban` 的独立默认侧栏保持可用。父项详情提供子事项分页与新建入口,子项通过 `planning.parentId` 关联父项,执行依赖仍单独保存在 `planning.dependencies`。
|
|
121
113
|
|
|
122
|
-
Task 的持久 startAt、编辑租约、单一状态投影与文件协议见 [执行规范](
|
|
114
|
+
Task 的持久 startAt、编辑租约、单一状态投影与文件协议见 [执行规范](https://github.com/nest-agentos/NestOS/blob/main/doc/design/kanban-task-execution.md)。`item().task` 和 query 的 `task` 为相同宿主事实的投影,`phase` 过滤在服务端执行。
|
|
115
|
+
|
|
116
|
+
事项支持 `request-control action=cancel` 的持久取消中状态及宿主停止回执;父项取消原子登记后代取消意图。`delete-item`、`delete-record` 保存逻辑删除墓碑,默认查询隐藏,`includeDeleted` 显式包含,原 ID 和审计保留。决策取消分为 `request-record-cancellation` 与结算后的 `cancel-record`。可选宿主端口 `controlRecord`、`taskFixes`、`cancelFix` 提供关联执行控制及修复澄清放弃;库不自行控制 Session。资源 `ownership` 独立于 `state`,归属未知或其他事项的资源必须保留,不得从归属未知推断执行已停止。
|
|
117
|
+
|
|
118
|
+
看板 `lifecycle` 支持整体取消与等待删除屏障:`request-board-stop` 冻结新任务/计划/准入/派发/续办,`observe-board-stop` 保存收尾阶段。宿主可提供 `cancelBoard`、`requestBoardDeletion`;后者保存跨重启删除请求,默认 drain 等待原执行结束,或显式 cancel 后再删除。独立库不自装调度器,宿主扫描负责最终预检和自动删除,UI 显示请求已接纳与具体阻塞项。
|
|
119
|
+
|
|
120
|
+
## 完整运行时
|
|
121
|
+
|
|
122
|
+
普通接入使用一个工厂,直接获得可查询、提交命令、扫描和关停的看板实例:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
import {createKanbanRuntime} from '@nestos/kanban/runtime';
|
|
126
|
+
|
|
127
|
+
const kanban = await createKanbanRuntime({
|
|
128
|
+
fs: authorizedFS,
|
|
129
|
+
actor: authenticatedPrincipal,
|
|
130
|
+
authorize: authorizeOperation,
|
|
131
|
+
execution: executionAdapter, // Optional for boards without automated execution.
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
await kanban.scan();
|
|
135
|
+
await kanban.dispose();
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
默认目录为 `/kanban`,可通过 `directory` 设置。`resources` 注入资源目录与权限校验,`activity` 注入执行活动来源,`onCommitted` 接入已有事件通道,`log` 接入诊断。库不自动创建定时器。返回值只包含看板语义操作,不暴露 store、journal、serviceFor 或原始 VFS。
|
|
139
|
+
|
|
140
|
+
执行适配器只实现以下协议:
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
const executionAdapter = {
|
|
144
|
+
catalog: () => authorizedTargets(),
|
|
145
|
+
prepare: intent => prepareExternalRequest(intent),
|
|
146
|
+
submit: handle => submitExternalRequest(handle),
|
|
147
|
+
inspect: handle => inspectExternalReceipt(handle),
|
|
148
|
+
control: (handle, command) => controlExternalExecution(handle, command),
|
|
149
|
+
};
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
- `ExecutionIntent` 包含稳定身份、作用域、目标与结构化上下文;`purpose` 区分规划和工作。规划返回 `ready/dependencies/reason` 及可选 estimate/subtasks,不引入宿主 Session/Flow DTO。
|
|
153
|
+
- `ExecutionHandle` 是可持久化的 `{id,data?}`,由适配器解释。prepare 不能启动外部执行;运行时先保存句柄再 submit,恢复时使用原句柄 inspect。
|
|
154
|
+
- 回执返回 `outcome`、可选 `result/executionId/code/controlCapabilities`。运行时保存并校验结果,通过 `kanban.results(boardId, dispatchId)` 读取授权产物。成功但结果未到达时保持 unknown;无正文结果显式返回 `result: null`。
|
|
155
|
+
- 控制统一接收句柄与 `{operationId,action}`。不提供 control 时返回明确的 `TASK_CONTROL_UNSUPPORTED`,不能把取消意图当作取消完成。
|
|
156
|
+
- 当前事项正文只放在 context.task 一处;决策只携带身份与修订,规划的 related 为摘要。执行依赖只传直接依赖的已验收产物。适配器按需通过授权查询读取额外内容。
|
|
157
|
+
|
|
158
|
+
运行时负责幂等认领、回执丢失恢复、日志投影、产物归属、取消协调和删除屏障。宿主无需了解持久键,也无需自行编写这些状态机。完整接入、重启恢复、失败处理和取消验收见 `tests/public-runtime.test.ts`。
|
|
159
|
+
|
|
160
|
+
## 自定义宿主集成
|
|
161
|
+
|
|
162
|
+
`/integration` 面向需要自定义预设、交互确认或底层装配的受信任宿主,显式导出 `assembleKanbanRuntime`、协调组件与语义状态接口。它与普通 `/runtime` 入口分开;远程 HTTP/MCP 服务应只暴露经过授权的看板语义操作,不能透传 integration 对象。
|
|
163
|
+
|
|
164
|
+
`KanbanCollaboration` 提供修复准备、冻结确认、接纳与决策快照,`collaborationRelations` 提供关联执行投影;适配器无需读取 fix、record-runs 或 task-reply 的内部持久键。宿主仍负责确认文案、通道交互和系统事件投影。
|
|
165
|
+
|
|
166
|
+
`KanbanOperationRecord` 与 `coordinateOperation` 提供自定义业务操作的统一持久协调;执行请求和结果保持不透明,业务插件负责结果约束。只有正式“未启动”回执允许重试提交,连接中断只核对原身份。
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { u as KanbanExternalReference, y as KanbanItemQuery, x as KanbanItemPage, z as KanbanItemSummary, r as KanbanDispatch, T as KanbanService, v as KanbanFixSession, f as KanbanAutomationCatalog, b as KanbanAnalysisStatus } from './planning-
|
|
1
|
+
import { u as KanbanExternalReference, y as KanbanItemQuery, x as KanbanItemPage, z as KanbanItemSummary, r as KanbanDispatch, T as KanbanService, v as KanbanFixSession, f as KanbanAutomationCatalog, b as KanbanAnalysisStatus } from './planning-CHrOqQ6b.cjs';
|
|
2
2
|
|
|
3
3
|
/** Hosts resolve opaque resources and execute records; the library owns no project policy. */
|
|
4
4
|
interface KanbanRecordResource {
|
|
@@ -18,7 +18,7 @@ interface KanbanRecordExecution {
|
|
|
18
18
|
boardId: string;
|
|
19
19
|
rootItemId?: string;
|
|
20
20
|
itemIds?: string[];
|
|
21
|
-
state: 'preparing' | 'running' | 'blocked' | 'review' | 'completed' | 'failed';
|
|
21
|
+
state: 'preparing' | 'running' | 'blocked' | 'review' | 'completed' | 'failed' | 'cancelling' | 'cancelled' | 'pausing' | 'paused' | 'resuming' | 'unknown';
|
|
22
22
|
createdAt: number;
|
|
23
23
|
updatedAt: number;
|
|
24
24
|
code?: string;
|
|
@@ -37,6 +37,7 @@ interface KanbanWorkResource {
|
|
|
37
37
|
title: string;
|
|
38
38
|
location: string;
|
|
39
39
|
version: string;
|
|
40
|
+
ownership?: 'current' | 'other' | 'unknown';
|
|
40
41
|
state: 'associated' | 'in-use' | 'retained' | 'cleanup-ready' | 'cleaned' | 'unknown';
|
|
41
42
|
reasons: string[];
|
|
42
43
|
properties: Array<{
|
|
@@ -118,6 +119,14 @@ interface KanbanDeletionPreview {
|
|
|
118
119
|
sessionId?: string;
|
|
119
120
|
}>;
|
|
120
121
|
pendingItems: number;
|
|
122
|
+
pendingDeletion?: {
|
|
123
|
+
operationId: string;
|
|
124
|
+
mode: 'drain' | 'cancel';
|
|
125
|
+
phase: 'waiting' | 'ready' | 'blocked';
|
|
126
|
+
code?: string;
|
|
127
|
+
};
|
|
128
|
+
/** Display labels for items referenced by blockers and resources. */
|
|
129
|
+
itemTitles?: Record<string, string>;
|
|
121
130
|
resources: Array<{
|
|
122
131
|
itemId: string;
|
|
123
132
|
resource: KanbanWorkResource;
|
|
@@ -201,6 +210,13 @@ interface KanbanExecutionSessionDetail {
|
|
|
201
210
|
}
|
|
202
211
|
/** Transport-facing service with optional authorized resource activity. */
|
|
203
212
|
interface KanbanClient extends KanbanService {
|
|
213
|
+
cancelBoard?(boardId: string, request: {
|
|
214
|
+
operationId: string;
|
|
215
|
+
expectedRevision: number;
|
|
216
|
+
}): Promise<void>;
|
|
217
|
+
requestBoardDeletion?(boardId: string, request: KanbanDeletionRequest & {
|
|
218
|
+
mode: 'drain' | 'cancel';
|
|
219
|
+
}): Promise<void>;
|
|
204
220
|
previewDeletion?(boardId: string): Promise<KanbanDeletionPreview>;
|
|
205
221
|
deleteBoard?(boardId: string, request: KanbanDeletionRequest): Promise<void>;
|
|
206
222
|
itemOperations?(boardId: string, itemId: string): Promise<KanbanItemOperations>;
|
|
@@ -215,6 +231,16 @@ interface KanbanClient extends KanbanService {
|
|
|
215
231
|
operationId: string;
|
|
216
232
|
content: string;
|
|
217
233
|
}): Promise<void>;
|
|
234
|
+
taskFixes?(boardId: string, itemId: string): Promise<KanbanFixSession[]>;
|
|
235
|
+
cancelFix?(boardId: string, request: {
|
|
236
|
+
fixId: string;
|
|
237
|
+
operationId: string;
|
|
238
|
+
}): Promise<KanbanFixSession>;
|
|
239
|
+
controlRecord?(boardId: string, request: {
|
|
240
|
+
recordId: string;
|
|
241
|
+
operationId: string;
|
|
242
|
+
action: 'pause' | 'resume' | 'cancel';
|
|
243
|
+
}): Promise<void>;
|
|
218
244
|
fixTask?(boardId: string, request: {
|
|
219
245
|
itemId: string;
|
|
220
246
|
operationId: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { u as KanbanExternalReference, y as KanbanItemQuery, x as KanbanItemPage, z as KanbanItemSummary, r as KanbanDispatch, T as KanbanService, v as KanbanFixSession, f as KanbanAutomationCatalog, b as KanbanAnalysisStatus } from './planning-
|
|
1
|
+
import { u as KanbanExternalReference, y as KanbanItemQuery, x as KanbanItemPage, z as KanbanItemSummary, r as KanbanDispatch, T as KanbanService, v as KanbanFixSession, f as KanbanAutomationCatalog, b as KanbanAnalysisStatus } from './planning-CHrOqQ6b.js';
|
|
2
2
|
|
|
3
3
|
/** Hosts resolve opaque resources and execute records; the library owns no project policy. */
|
|
4
4
|
interface KanbanRecordResource {
|
|
@@ -18,7 +18,7 @@ interface KanbanRecordExecution {
|
|
|
18
18
|
boardId: string;
|
|
19
19
|
rootItemId?: string;
|
|
20
20
|
itemIds?: string[];
|
|
21
|
-
state: 'preparing' | 'running' | 'blocked' | 'review' | 'completed' | 'failed';
|
|
21
|
+
state: 'preparing' | 'running' | 'blocked' | 'review' | 'completed' | 'failed' | 'cancelling' | 'cancelled' | 'pausing' | 'paused' | 'resuming' | 'unknown';
|
|
22
22
|
createdAt: number;
|
|
23
23
|
updatedAt: number;
|
|
24
24
|
code?: string;
|
|
@@ -37,6 +37,7 @@ interface KanbanWorkResource {
|
|
|
37
37
|
title: string;
|
|
38
38
|
location: string;
|
|
39
39
|
version: string;
|
|
40
|
+
ownership?: 'current' | 'other' | 'unknown';
|
|
40
41
|
state: 'associated' | 'in-use' | 'retained' | 'cleanup-ready' | 'cleaned' | 'unknown';
|
|
41
42
|
reasons: string[];
|
|
42
43
|
properties: Array<{
|
|
@@ -118,6 +119,14 @@ interface KanbanDeletionPreview {
|
|
|
118
119
|
sessionId?: string;
|
|
119
120
|
}>;
|
|
120
121
|
pendingItems: number;
|
|
122
|
+
pendingDeletion?: {
|
|
123
|
+
operationId: string;
|
|
124
|
+
mode: 'drain' | 'cancel';
|
|
125
|
+
phase: 'waiting' | 'ready' | 'blocked';
|
|
126
|
+
code?: string;
|
|
127
|
+
};
|
|
128
|
+
/** Display labels for items referenced by blockers and resources. */
|
|
129
|
+
itemTitles?: Record<string, string>;
|
|
121
130
|
resources: Array<{
|
|
122
131
|
itemId: string;
|
|
123
132
|
resource: KanbanWorkResource;
|
|
@@ -201,6 +210,13 @@ interface KanbanExecutionSessionDetail {
|
|
|
201
210
|
}
|
|
202
211
|
/** Transport-facing service with optional authorized resource activity. */
|
|
203
212
|
interface KanbanClient extends KanbanService {
|
|
213
|
+
cancelBoard?(boardId: string, request: {
|
|
214
|
+
operationId: string;
|
|
215
|
+
expectedRevision: number;
|
|
216
|
+
}): Promise<void>;
|
|
217
|
+
requestBoardDeletion?(boardId: string, request: KanbanDeletionRequest & {
|
|
218
|
+
mode: 'drain' | 'cancel';
|
|
219
|
+
}): Promise<void>;
|
|
204
220
|
previewDeletion?(boardId: string): Promise<KanbanDeletionPreview>;
|
|
205
221
|
deleteBoard?(boardId: string, request: KanbanDeletionRequest): Promise<void>;
|
|
206
222
|
itemOperations?(boardId: string, itemId: string): Promise<KanbanItemOperations>;
|
|
@@ -215,6 +231,16 @@ interface KanbanClient extends KanbanService {
|
|
|
215
231
|
operationId: string;
|
|
216
232
|
content: string;
|
|
217
233
|
}): Promise<void>;
|
|
234
|
+
taskFixes?(boardId: string, itemId: string): Promise<KanbanFixSession[]>;
|
|
235
|
+
cancelFix?(boardId: string, request: {
|
|
236
|
+
fixId: string;
|
|
237
|
+
operationId: string;
|
|
238
|
+
}): Promise<KanbanFixSession>;
|
|
239
|
+
controlRecord?(boardId: string, request: {
|
|
240
|
+
recordId: string;
|
|
241
|
+
operationId: string;
|
|
242
|
+
action: 'pause' | 'resume' | 'cancel';
|
|
243
|
+
}): Promise<void>;
|
|
218
244
|
fixTask?(boardId: string, request: {
|
|
219
245
|
itemId: string;
|
|
220
246
|
operationId: string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { p as KanbanSessionActivityQuery, t as KanbanSessionItemPage, r as KanbanSessionDetailQuery, d as KanbanExecutionSessionDetail } from './activity-BbC4sgKL.cjs';
|
|
2
|
+
import { K as KanbanModuleHandle } from './module-Ca9L3qCn.cjs';
|
|
3
|
+
import { K as KanbanModuleOptions } from './ports-GQy0x-RI.cjs';
|
|
4
|
+
import { K as KanbanSessionSource } from './activity-ports-Dqxa86zT.cjs';
|
|
5
|
+
|
|
6
|
+
interface KanbanSessionActivityOptions {
|
|
7
|
+
source: KanbanSessionSource;
|
|
8
|
+
now?: () => number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface KanbanActivityModuleOptions extends KanbanModuleOptions {
|
|
12
|
+
executionSessions?: KanbanSessionActivityOptions;
|
|
13
|
+
referenceSystem: string;
|
|
14
|
+
}
|
|
15
|
+
interface KanbanActivityHandle extends KanbanModuleHandle {
|
|
16
|
+
queryActivity: (boardId: string, query?: KanbanSessionActivityQuery) => Promise<KanbanSessionItemPage>;
|
|
17
|
+
executionSession: (boardId: string, itemId: string, query?: KanbanSessionDetailQuery) => Promise<KanbanExecutionSessionDetail>;
|
|
18
|
+
}
|
|
19
|
+
/** Application integrations stay outside the portable core. */
|
|
20
|
+
declare function createKanbanActivityModule(options: KanbanActivityModuleOptions): KanbanActivityHandle;
|
|
21
|
+
|
|
22
|
+
export { type KanbanActivityHandle as K, type KanbanActivityModuleOptions as a, type KanbanSessionActivityOptions as b, createKanbanActivityModule as c };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { p as KanbanSessionActivityQuery, t as KanbanSessionItemPage, r as KanbanSessionDetailQuery, d as KanbanExecutionSessionDetail } from './activity-Czlan5xW.js';
|
|
2
|
+
import { K as KanbanModuleHandle } from './module-CYfE7YhM.js';
|
|
3
|
+
import { K as KanbanModuleOptions } from './ports-B0McVg7B.js';
|
|
4
|
+
import { K as KanbanSessionSource } from './activity-ports-cxfrelaL.js';
|
|
5
|
+
|
|
6
|
+
interface KanbanSessionActivityOptions {
|
|
7
|
+
source: KanbanSessionSource;
|
|
8
|
+
now?: () => number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface KanbanActivityModuleOptions extends KanbanModuleOptions {
|
|
12
|
+
executionSessions?: KanbanSessionActivityOptions;
|
|
13
|
+
referenceSystem: string;
|
|
14
|
+
}
|
|
15
|
+
interface KanbanActivityHandle extends KanbanModuleHandle {
|
|
16
|
+
queryActivity: (boardId: string, query?: KanbanSessionActivityQuery) => Promise<KanbanSessionItemPage>;
|
|
17
|
+
executionSession: (boardId: string, itemId: string, query?: KanbanSessionDetailQuery) => Promise<KanbanExecutionSessionDetail>;
|
|
18
|
+
}
|
|
19
|
+
/** Application integrations stay outside the portable core. */
|
|
20
|
+
declare function createKanbanActivityModule(options: KanbanActivityModuleOptions): KanbanActivityHandle;
|
|
21
|
+
|
|
22
|
+
export { type KanbanActivityHandle as K, type KanbanActivityModuleOptions as a, type KanbanSessionActivityOptions as b, createKanbanActivityModule as c };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { j as KanbanBoard, w as KanbanItem, a7 as KanbanWorkflowState } from './planning-CHrOqQ6b.cjs';
|
|
2
|
+
import { q as KanbanSessionAssociation, c as KanbanExecutionSession } from './activity-BbC4sgKL.cjs';
|
|
3
|
+
|
|
4
|
+
interface KanbanSessionSource {
|
|
5
|
+
/** Cold, authorized relations only; never submit or restore execution while listing. */
|
|
6
|
+
related?(board: KanbanBoard, item: KanbanItem, workflow: KanbanWorkflowState): Promise<KanbanSessionAssociation[]>;
|
|
7
|
+
authorizeBinding?(board: KanbanBoard, sessionId: string): Promise<void>;
|
|
8
|
+
/** Must check access within the supplied board's user/project and authority. */
|
|
9
|
+
inspect(board: KanbanBoard, sessionId: string): Promise<Omit<KanbanExecutionSession, 'recent'>>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type { KanbanSessionSource as K };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { j as KanbanBoard, w as KanbanItem, a7 as KanbanWorkflowState } from './planning-CHrOqQ6b.js';
|
|
2
|
+
import { q as KanbanSessionAssociation, c as KanbanExecutionSession } from './activity-Czlan5xW.js';
|
|
3
|
+
|
|
4
|
+
interface KanbanSessionSource {
|
|
5
|
+
/** Cold, authorized relations only; never submit or restore execution while listing. */
|
|
6
|
+
related?(board: KanbanBoard, item: KanbanItem, workflow: KanbanWorkflowState): Promise<KanbanSessionAssociation[]>;
|
|
7
|
+
authorizeBinding?(board: KanbanBoard, sessionId: string): Promise<void>;
|
|
8
|
+
/** Must check access within the supplied board's user/project and authority. */
|
|
9
|
+
inspect(board: KanbanBoard, sessionId: string): Promise<Omit<KanbanExecutionSession, 'recent'>>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type { KanbanSessionSource as K };
|
package/dist/activity.cjs
CHANGED
|
@@ -1,288 +1,16 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
2
|
|
|
3
|
-
var _chunkIUI5CI2Scjs = require('./chunk-IUI5CI2S.cjs');
|
|
4
|
-
require('./chunk-L44TM4EE.cjs');
|
|
5
|
-
require('./chunk-4XF6LQSU.cjs');
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
var _chunk4KF7KUAIcjs = require('./chunk-4KF7KUAI.cjs');
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
var _chunk2YFPV5GFcjs = require('./chunk-2YFPV5GF.cjs');
|
|
14
|
-
require('./chunk-KYNYPKKH.cjs');
|
|
15
|
-
require('./chunk-PSKQAFDF.cjs');
|
|
16
3
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
require('./chunk-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (!["all", "running", "recent", "active"].includes(execution)) throw new (0, _chunk2YFPV5GFcjs.KanbanError)("INVALID_EXECUTION_FILTER");
|
|
26
|
-
if (execution === "all") {
|
|
27
|
-
const page2 = await core.query(boardId, query);
|
|
28
|
-
return { ...page2, items: await project(page2.items) };
|
|
29
|
-
}
|
|
30
|
-
const { cursor, limit = 50, ...filters } = query;
|
|
31
|
-
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 200) throw new (0, _chunk2YFPV5GFcjs.KanbanError)("INVALID_PAGE_SIZE");
|
|
32
|
-
let page = await core.query(boardId, { ...filters, limit: 200 }), rows = [...page.items];
|
|
33
|
-
const revision = page.revision;
|
|
34
|
-
while (page.nextCursor) {
|
|
35
|
-
if (rows.length >= 1e4) throw new (0, _chunk2YFPV5GFcjs.KanbanError)("ACTIVITY_QUERY_LIMIT");
|
|
36
|
-
page = await core.query(boardId, { ...filters, limit: 200, cursor: page.nextCursor });
|
|
37
|
-
if (page.revision !== revision) throw new (0, _chunk2YFPV5GFcjs.KanbanError)("CURSOR_STALE");
|
|
38
|
-
rows.push(...page.items);
|
|
39
|
-
}
|
|
40
|
-
const matching = (await project(rows)).filter((row) => matchesExecution(row, execution));
|
|
41
|
-
if ((await core.query(boardId, { ...filters, limit: 1 })).revision !== revision) throw new (0, _chunk2YFPV5GFcjs.KanbanError)("CURSOR_STALE");
|
|
42
|
-
const scope = _chunk6MN3H3YFcjs.sha256HexSync.call(void 0, JSON.stringify({ filters, execution, limit })), signature = _chunk6MN3H3YFcjs.sha256HexSync.call(void 0, JSON.stringify(matching.map((row) => row.id)));
|
|
43
|
-
let start = 0;
|
|
44
|
-
if (cursor) {
|
|
45
|
-
let saved;
|
|
46
|
-
try {
|
|
47
|
-
saved = JSON.parse(cursor);
|
|
48
|
-
} catch (e) {
|
|
49
|
-
throw new (0, _chunk2YFPV5GFcjs.KanbanError)("INVALID_CURSOR");
|
|
50
|
-
}
|
|
51
|
-
if (saved.scope !== scope) throw new (0, _chunk2YFPV5GFcjs.KanbanError)("CURSOR_SCOPE_MISMATCH");
|
|
52
|
-
if (saved.revision !== revision || saved.signature !== signature) throw new (0, _chunk2YFPV5GFcjs.KanbanError)("CURSOR_STALE");
|
|
53
|
-
start = matching.findIndex((row) => row.id === saved.after) + 1;
|
|
54
|
-
if (start < 1) throw new (0, _chunk2YFPV5GFcjs.KanbanError)("INVALID_CURSOR");
|
|
55
|
-
}
|
|
56
|
-
const items = matching.slice(start, start + limit);
|
|
57
|
-
return { revision, total: matching.length, items, ...start + items.length < matching.length ? { nextCursor: JSON.stringify({ scope, revision, signature, after: items.at(-1).id }) } : {} };
|
|
58
|
-
}
|
|
59
|
-
function matchesExecution(row, filter) {
|
|
60
|
-
const session = row.execution;
|
|
61
|
-
if (!session || session.stale) return false;
|
|
62
|
-
return filter === "running" ? session.state === "running" : filter === "recent" ? session.recent : session.state === "running" || session.recent;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// src/session-relations.ts
|
|
66
|
-
function sessionAssociations(item, bindings, workflow) {
|
|
67
|
-
return [...bindings.map((ref) => {
|
|
68
|
-
const dispatch = workflow.dispatches[_nullishCoalesce(ref.revision, () => ( ""))];
|
|
69
|
-
const execution = _optionalChain([dispatch, 'optionalAccess', _ => _.itemId]) === item.id ? {
|
|
70
|
-
id: dispatch.id,
|
|
71
|
-
purpose: "execution",
|
|
72
|
-
dispatchId: dispatch.id,
|
|
73
|
-
attempt: dispatch.attempt,
|
|
74
|
-
outcome: dispatch.outcome,
|
|
75
|
-
createdAt: dispatch.preparedAt,
|
|
76
|
-
code: dispatch.code,
|
|
77
|
-
...dispatch.continuation ? { previousDispatchId: dispatch.continuation.previousDispatchId } : {}
|
|
78
|
-
} : void 0;
|
|
79
|
-
return { sessionId: ref.id, purpose: "execution", ...execution ? { execution } : {} };
|
|
80
|
-
}), ...item.participants.filter((row) => !bindings.some((ref) => ref.id === row.sessionId)).map((row) => ({ sessionId: row.sessionId, purpose: "discussion" }))];
|
|
81
|
-
}
|
|
82
|
-
async function projectRelatedSessions(associations, currentId, inspect) {
|
|
83
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
84
|
-
for (const row of associations) {
|
|
85
|
-
let group = grouped.get(row.sessionId);
|
|
86
|
-
if (!group) grouped.set(row.sessionId, group = { purposes: /* @__PURE__ */ new Set(), executions: /* @__PURE__ */ new Map() });
|
|
87
|
-
group.purposes.add(row.purpose);
|
|
88
|
-
if (row.execution) group.executions.set(row.execution.id, { ...group.executions.get(row.execution.id), ...row.execution });
|
|
89
|
-
}
|
|
90
|
-
const rows = [], entries = [...grouped];
|
|
91
|
-
let next = 0;
|
|
92
|
-
await Promise.all(Array.from({ length: Math.min(entries.length, 8) }, async () => {
|
|
93
|
-
while (next < entries.length) {
|
|
94
|
-
const [sessionId, group] = entries[next++], state = await inspect(sessionId);
|
|
95
|
-
rows.push({
|
|
96
|
-
...state,
|
|
97
|
-
current: sessionId === currentId,
|
|
98
|
-
purposes: [...group.purposes].sort(),
|
|
99
|
-
executions: [...group.executions.values()].sort((a, b) => (_nullishCoalesce(a.createdAt, () => ( 0))) - (_nullishCoalesce(b.createdAt, () => ( 0))) || a.id.localeCompare(b.id))
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
}));
|
|
103
|
-
return rows.sort((a, b) => Number(b.current) - Number(a.current) || a.sessionId.localeCompare(b.sessionId));
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// src/session-activity.ts
|
|
107
|
-
function executionBindings(refs, referenceSystem) {
|
|
108
|
-
return (_nullishCoalesce(refs, () => ( []))).filter((ref) => ref.system === referenceSystem);
|
|
109
|
-
}
|
|
110
|
-
function currentBinding(refs, itemId, workflow, referenceSystem) {
|
|
111
|
-
const binding = executionBindings(refs, referenceSystem).at(-1), latest = Object.values(workflow.dispatches).filter((dispatch) => dispatch.itemId === itemId).sort((a, b) => b.attempt - a.attempt)[0];
|
|
112
|
-
if (!binding || !latest || binding.revision === latest.id) return binding;
|
|
113
|
-
return ["prepared", "running", "unknown", "succeeded"].includes(latest.outcome) || workflow.dispatches[_nullishCoalesce(binding.revision, () => ( ""))] ? void 0 : binding;
|
|
114
|
-
}
|
|
115
|
-
function createKanbanSessionActivity(core, options, log, referenceSystem) {
|
|
116
|
-
const inspect = createSessionInspector(options, log);
|
|
117
|
-
const project = async (boardId, rows) => {
|
|
118
|
-
if (!rows.some((row) => executionBindings(row.externalRefs, referenceSystem).length)) return rows;
|
|
119
|
-
const [board, workflow] = await Promise.all([boardMetadata(core, boardId), core.workflow(boardId)]), bindings = new Map(rows.map((row) => [row.id, currentBinding(row.externalRefs, row.id, workflow, referenceSystem)]));
|
|
120
|
-
const ids = [...new Set([...bindings.values()].flatMap((ref) => ref ? [ref.id] : []))], states = /* @__PURE__ */ new Map();
|
|
121
|
-
let next = 0;
|
|
122
|
-
await Promise.all(Array.from({ length: Math.min(ids.length, 8) }, async () => {
|
|
123
|
-
while (next < ids.length) {
|
|
124
|
-
const id = ids[next++];
|
|
125
|
-
states.set(id, await inspect(board, id));
|
|
126
|
-
}
|
|
127
|
-
}));
|
|
128
|
-
return rows.map((row) => {
|
|
129
|
-
const binding = bindings.get(row.id);
|
|
130
|
-
return { ...row, ...binding ? { execution: states.get(binding.id) } : {} };
|
|
131
|
-
});
|
|
132
|
-
};
|
|
133
|
-
return {
|
|
134
|
-
queryActivity: (boardId, query = {}) => querySessionItems(core, boardId, query, (rows) => project(boardId, rows)),
|
|
135
|
-
async executionSession(boardId, itemId, query = {}) {
|
|
136
|
-
const started = Date.now();
|
|
137
|
-
if (!query || typeof query !== "object" || Array.isArray(query) || Object.keys(query).some((key) => key !== "includeRelated") || query.includeRelated !== void 0 && typeof query.includeRelated !== "boolean") throw new (0, _chunk2YFPV5GFcjs.KanbanError)("INVALID_QUERY");
|
|
138
|
-
const { item } = await core.item(boardId, itemId), bindings = executionBindings(item.externalRefs, referenceSystem);
|
|
139
|
-
if (!bindings.length && !query.includeRelated) return { history: [] };
|
|
140
|
-
const [board, workflow] = await Promise.all([boardMetadata(core, boardId), core.workflow(boardId)]), last = currentBinding(item.externalRefs, itemId, workflow, referenceSystem);
|
|
141
|
-
let sessionsCode;
|
|
142
|
-
const related = query.includeRelated ? await _optionalChain([options, 'optionalAccess', _2 => _2.source, 'access', _3 => _3.related, 'optionalCall', _4 => _4(board, item, workflow), 'access', _5 => _5.catch, 'call', _6 => _6((error) => {
|
|
143
|
-
const candidate = _optionalChain([error, 'optionalAccess', _7 => _7.code]);
|
|
144
|
-
sessionsCode = candidate && /^[A-Z][A-Z0-9_]{0,79}$/.test(candidate) ? candidate : "SESSION_RELATIONS_FAILED";
|
|
145
|
-
try {
|
|
146
|
-
log({ operationId: _chunk6MN3H3YFcjs.randomUUID.call(void 0, ), stage: "session.relations", result: "failure", code: sessionsCode, durationMs: Date.now() - started });
|
|
147
|
-
} catch (e2) {
|
|
148
|
-
}
|
|
149
|
-
return [];
|
|
150
|
-
})]) : [];
|
|
151
|
-
const sessions = query.includeRelated ? await projectRelatedSessions([
|
|
152
|
-
...sessionAssociations(item, bindings, workflow),
|
|
153
|
-
..._nullishCoalesce(related, () => ( []))
|
|
154
|
-
], _optionalChain([last, 'optionalAccess', _8 => _8.id]), (id) => inspect(board, id)) : void 0;
|
|
155
|
-
return {
|
|
156
|
-
current: last ? await _asyncNullishCoalesce(await _asyncOptionalChain([sessions, 'optionalAccess', async _9 => _9.find, 'call', async _10 => _10((row) => row.sessionId === last.id)]), async () => ( await inspect(board, last.id))) : void 0,
|
|
157
|
-
history: bindings.map((ref) => ({ sessionId: ref.id, bindingId: ref.revision, current: ref === last })).reverse(),
|
|
158
|
-
...sessions ? { sessions } : {},
|
|
159
|
-
...sessionsCode ? { sessionsCode } : {}
|
|
160
|
-
};
|
|
161
|
-
},
|
|
162
|
-
validate: (board, command, actor) => validateBinding(core, options, board, command, actor, referenceSystem)
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
function createSessionInspector(options, log) {
|
|
166
|
-
const now = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _11 => _11.now]), () => ( Date.now)), cache = /* @__PURE__ */ new Map();
|
|
167
|
-
return async (board, sessionId) => {
|
|
168
|
-
const started = now(), key = JSON.stringify([board.id, sessionId]);
|
|
169
|
-
try {
|
|
170
|
-
if (!options) throw new (0, _chunk2YFPV5GFcjs.KanbanError)("EXECUTION_SESSION_UNAVAILABLE");
|
|
171
|
-
const row = await options.source.inspect(board, sessionId);
|
|
172
|
-
if (row.sessionId !== sessionId) throw new (0, _chunk2YFPV5GFcjs.KanbanError)("SESSION_ACTIVITY_SCOPE_MISMATCH");
|
|
173
|
-
const stale = row.stale || row.observedAt === void 0 || now() - row.observedAt > 9e4;
|
|
174
|
-
const value = {
|
|
175
|
-
sessionId,
|
|
176
|
-
...typeof row.resourceId === "string" && row.resourceId.length <= 4096 && !row.resourceId.includes("\0") ? { resourceId: row.resourceId } : {},
|
|
177
|
-
title: _optionalChain([row, 'access', _12 => _12.title, 'optionalAccess', _13 => _13.slice, 'call', _14 => _14(0, 256)]),
|
|
178
|
-
state: row.state,
|
|
179
|
-
createdAt: row.createdAt,
|
|
180
|
-
lastActivityAt: row.lastActivityAt,
|
|
181
|
-
observedAt: row.observedAt,
|
|
182
|
-
stale,
|
|
183
|
-
recent: !stale && row.lastActivityAt !== void 0 && row.lastActivityAt <= now() && now() - row.lastActivityAt <= 6e5,
|
|
184
|
-
...row.code && /^[A-Z][A-Z0-9_]{0,79}$/.test(row.code) ? { code: row.code } : {}
|
|
185
|
-
};
|
|
186
|
-
if (cache.size >= 256) cache.delete(cache.keys().next().value);
|
|
187
|
-
cache.set(key, value);
|
|
188
|
-
return value;
|
|
189
|
-
} catch (error) {
|
|
190
|
-
const candidate = _optionalChain([error, 'optionalAccess', _15 => _15.code]), code = candidate && /^[A-Z][A-Z0-9_]{0,79}$/.test(candidate) ? candidate : "SESSION_ACTIVITY_FAILED";
|
|
191
|
-
try {
|
|
192
|
-
log({ operationId: _chunk6MN3H3YFcjs.randomUUID.call(void 0, ), sessionId, stage: "session.activity", result: "failure", code, durationMs: now() - started });
|
|
193
|
-
} catch (e3) {
|
|
194
|
-
}
|
|
195
|
-
const prior = ["EIO", "ETIMEDOUT", "ECONNRESET", "ECONNREFUSED", "SESSION_ACTIVITY_FAILED"].includes(code) ? cache.get(key) : void 0;
|
|
196
|
-
return { ...prior, sessionId, state: _nullishCoalesce(_optionalChain([prior, 'optionalAccess', _16 => _16.state]), () => ( "unknown")), stale: true, recent: false, code };
|
|
197
|
-
}
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
async function boardMetadata(core, boardId) {
|
|
201
|
-
const board = await _asyncNullishCoalesce((await core.catalog()).find((board2) => board2.id === boardId), async () => ( await core.read(boardId)));
|
|
202
|
-
return { ...board, items: [] };
|
|
203
|
-
}
|
|
204
|
-
async function validateBinding(core, options, board, command, actor, referenceSystem) {
|
|
205
|
-
if (_optionalChain([command, 'optionalAccess', _17 => _17.type]) !== "link-reference" || command.reference.system !== referenceSystem) return;
|
|
206
|
-
if (!options || !command.reference.revision) throw new (0, _chunk2YFPV5GFcjs.KanbanError)("EXECUTION_SESSION_UNAVAILABLE");
|
|
207
|
-
const item = board.items.find((item2) => item2.id === command.itemId);
|
|
208
|
-
if (!item) throw new (0, _chunk2YFPV5GFcjs.KanbanError)("ITEM_NOT_FOUND");
|
|
209
|
-
const key = _chunk4KF7KUAIcjs.principalKey.call(void 0, _chunk4KF7KUAIcjs.principal.call(void 0, actor)), coordinator = _optionalChain([board, 'access', _18 => _18.members, 'optionalAccess', _19 => _19.some, 'call', _20 => _20((member) => _chunk4KF7KUAIcjs.principalKey.call(void 0, member) === key && _optionalChain([member, 'access', _21 => _21.roles, 'optionalAccess', _22 => _22.includes, 'call', _23 => _23("coordinator")]))]);
|
|
210
|
-
if (!coordinator && _optionalChain([item, 'access', _24 => _24.owner, 'optionalAccess', _25 => _25.principalId]) !== key && !(item.owner === void 0 && _chunk4KF7KUAIcjs.candidates.call(void 0, board, item).some((candidate) => _chunk4KF7KUAIcjs.principalKey.call(void 0, candidate) === key))) throw new (0, _chunk2YFPV5GFcjs.KanbanError)("EXECUTION_SESSION_ASSIGNMENT_DENIED");
|
|
211
|
-
const active = Object.values((await core.workflow(board.id)).dispatches).filter((dispatch) => dispatch.itemId === item.id).sort((a, b) => b.attempt - a.attempt)[0];
|
|
212
|
-
if (active && command.reference.revision !== active.id) throw new (0, _chunk2YFPV5GFcjs.KanbanError)("EXECUTION_SESSION_LOCKED");
|
|
213
|
-
if (active && executionBindings(item.externalRefs, referenceSystem).some((ref) => ref.revision === active.id && ref.id !== command.reference.id)) throw new (0, _chunk2YFPV5GFcjs.KanbanError)("EXECUTION_SESSION_LOCKED");
|
|
214
|
-
if (options.source.authorizeBinding) return options.source.authorizeBinding(board, command.reference.id);
|
|
215
|
-
const state = await options.source.inspect(board, command.reference.id);
|
|
216
|
-
if (state.sessionId !== command.reference.id || state.stale) throw new (0, _chunk2YFPV5GFcjs.KanbanError)("EXECUTION_SESSION_UNAVAILABLE");
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// src/activity-module.ts
|
|
220
|
-
function createKanbanActivityModule(options) {
|
|
221
|
-
const { executionSessions, referenceSystem, ...core } = options;
|
|
222
|
-
let activity;
|
|
223
|
-
const service = _chunkIUI5CI2Scjs.createKanbanModule.call(void 0, {
|
|
224
|
-
...core,
|
|
225
|
-
authorize: async (input) => {
|
|
226
|
-
await core.authorize(input);
|
|
227
|
-
await activity.validate(input.board, input.command, input.actor);
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
activity = createKanbanSessionActivity(service, executionSessions, core.log, referenceSystem);
|
|
231
|
-
let stopping = false;
|
|
232
|
-
const pending = /* @__PURE__ */ new Set();
|
|
233
|
-
const track = (run) => {
|
|
234
|
-
if (stopping) return Promise.reject(new (0, _chunk2YFPV5GFcjs.KanbanError)("SERVICE_STOPPING"));
|
|
235
|
-
const work = Promise.resolve().then(run).finally(() => {
|
|
236
|
-
pending.delete(work);
|
|
237
|
-
});
|
|
238
|
-
pending.add(work);
|
|
239
|
-
return work;
|
|
240
|
-
};
|
|
241
|
-
const quiesce = async () => {
|
|
242
|
-
stopping = true;
|
|
243
|
-
await Promise.allSettled([...pending]);
|
|
244
|
-
await service.quiesce();
|
|
245
|
-
};
|
|
246
|
-
return {
|
|
247
|
-
...forwardCore(service, track),
|
|
248
|
-
queryActivity: (...args) => track(() => activity.queryActivity(...args)),
|
|
249
|
-
executionSession: (...args) => track(() => activity.executionSession(...args)),
|
|
250
|
-
related: (sessionId) => track(() => relatedBoards(service, referenceSystem, sessionId)),
|
|
251
|
-
quiesce,
|
|
252
|
-
dispose: quiesce
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
function forwardCore(service, track) {
|
|
256
|
-
return {
|
|
257
|
-
create: (input) => track(() => service.create(input)),
|
|
258
|
-
read: (id) => track(() => service.read(id)),
|
|
259
|
-
list: () => track(() => service.list()),
|
|
260
|
-
catalog: () => track(() => service.catalog()),
|
|
261
|
-
execute: (id, request) => track(() => service.execute(id, request)),
|
|
262
|
-
events: (id, after, limit) => track(() => service.events(id, after, limit)),
|
|
263
|
-
query: (id, query) => track(() => service.query(id, query)),
|
|
264
|
-
item: (id, item) => track(() => service.item(id, item)),
|
|
265
|
-
overview: (id) => track(() => service.overview(id)),
|
|
266
|
-
scan: () => track(() => service.scan()),
|
|
267
|
-
workflow: (id) => track(() => service.workflow(id)),
|
|
268
|
-
resolve: (reference) => track(() => service.resolve(reference)),
|
|
269
|
-
suggest: (id, query) => track(() => service.suggest(id, query)),
|
|
270
|
-
records: (id, query) => track(() => service.records(id, query)),
|
|
271
|
-
record: (id, record) => track(() => service.record(id, record)),
|
|
272
|
-
currentPrincipal: () => track(() => service.currentPrincipal())
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
async function relatedBoards(service, referenceSystem, sessionId) {
|
|
276
|
-
const boards = await service.related(sessionId);
|
|
277
|
-
for (const board of await service.catalog()) {
|
|
278
|
-
if (boards.some((old) => old.id === board.id)) continue;
|
|
279
|
-
const matches = await service.query(board.id, { view: "all", reference: { system: referenceSystem, id: sessionId }, limit: 1 });
|
|
280
|
-
if (matches.total) boards.push(await service.read(board.id));
|
|
281
|
-
}
|
|
282
|
-
return boards;
|
|
283
|
-
}
|
|
4
|
+
var _chunk5Q7WB63Acjs = require('./chunk-5Q7WB63A.cjs');
|
|
5
|
+
require('./chunk-DINLR6E6.cjs');
|
|
6
|
+
require('./chunk-EYTVC2H5.cjs');
|
|
7
|
+
require('./chunk-4XF6LQSU.cjs');
|
|
8
|
+
require('./chunk-RBOER6EW.cjs');
|
|
9
|
+
require('./chunk-7KDOTBRX.cjs');
|
|
10
|
+
require('./chunk-RSH6QSEC.cjs');
|
|
11
|
+
require('./chunk-AOHQCX7K.cjs');
|
|
284
12
|
|
|
285
13
|
|
|
286
14
|
|
|
287
|
-
exports.createKanbanActivityModule = createKanbanActivityModule; exports.querySessionItems = querySessionItems;
|
|
15
|
+
exports.createKanbanActivityModule = _chunk5Q7WB63Acjs.createKanbanActivityModule; exports.querySessionItems = _chunk5Q7WB63Acjs.querySessionItems;
|
|
288
16
|
//# sourceMappingURL=activity.cjs.map
|