@coda-console/protocol 1.10.0 → 2.1.0
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/README.md +19 -2
- package/docs/executor-protocol.md +223 -0
- package/executor.mjs +21 -0
- package/package.json +5 -3
- package/schemas/executor/v1/common.json +84 -0
- package/schemas/executor/v1/execution.json +199 -0
- package/schemas/executor/v1/executor.json +95 -0
- package/schemas/plugin/agents.json +1 -1
- package/schemas/plugin/capabilities.json +1 -1
- package/schemas/plugin/commands.json +1 -1
- package/schemas/plugin/common.json +1 -1
- package/schemas/plugin/composer-modes.json +30 -13
- package/schemas/plugin/configuration.json +1 -1
- package/schemas/plugin/endpoints.json +1 -1
- package/schemas/plugin/environment.json +1 -1
- package/schemas/plugin/events.json +1 -1
- package/schemas/plugin/executors.json +1 -1
- package/schemas/plugin/extension-points.json +1 -1
- package/schemas/plugin/extensions.json +1 -1
- package/schemas/plugin/git-credentials.json +1 -1
- package/schemas/plugin/groups.json +1 -1
- package/schemas/plugin/instructions.json +1 -1
- package/schemas/plugin/mcp-servers.json +1 -1
- package/schemas/plugin/model-providers.json +1 -1
- package/schemas/plugin/path.json +1 -1
- package/schemas/plugin/plugin.json +1 -1
- package/schemas/plugin/repositories.json +1 -1
- package/schemas/plugin/services.json +1 -1
- package/schemas/plugin/setting.json +2 -2
- package/schemas/plugin/setup.json +1 -1
- package/schemas/plugin/skills.json +1 -1
- package/schemas/plugin/views.json +1 -1
- package/validate.mjs +2 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Coda 与插件之间的约定,Coda 和插件都引用它,独立演进:
|
|
4
4
|
|
|
5
|
-
- **插件清单的类型**(`schemas/plugin/`):插件目录 `package.json` 里 `contributes` 的每个键是一种类型,各有一份 JSON Schema(draft 2020-12,`$id` 为 `https://coda.pub/schemas/plugin/
|
|
5
|
+
- **插件清单的类型**(`schemas/plugin/`):插件目录 `package.json` 里 `contributes` 的每个键是一种类型,各有一份 JSON Schema(draft 2020-12,`$id` 为 `https://coda.pub/schemas/plugin/v2/<文件>`)。`plugin.json` 是整份清单,`setting.json` 是一项设置的声明。插件写的合乎它用到的类型就对;写给别的插件扩展点的键由那个插件声明,扩展点可以带自己的 schema(`extensionPoints.<名>.schema`,声明它的插件目录里的一个文件),写给它的东西合乎那份 schema 才对。
|
|
6
6
|
- **通用校验器**(`validate.mjs`):用 Ajv 对清单做 schema 校验。JSON Schema 说不了的只用五个通用关键字(`x-unique-by`、`x-max-contains`、`x-ref`、`x-placeholders`、`x-file`,照 Kubernetes 的 `x-kubernetes-*`),每个在哪里写都是同一个意思,写法见文件开头。校验器不随功能变长:类型加字段就是改 schema。
|
|
7
7
|
- **流程方言**(`workflow/`):项目和插件的流程(`.coda/workflows/*.bpmn`,BPMN 2.0)在 Coda 的命名空间 `https://coda.pub/schema/bpmn/1.0` 里能写什么。`moddle.json` 是 `coda:` 属性与元素(bpmn-moddle 的描述格式),`values.json` 是每个值能写什么(每个 `coda:` 类型的属性组成的对象的 JSON Schema,一个元素里同种 `coda:` 元素成组的 schema),`actions.json` 是 `coda:action` 的各个动作和它们接受的参数,`dialect.json` 是流程种类、Coda 自己发的事件、步骤失败的错误码、表达式能读的运行变量和运行的输入能是哪些类型。`workflow.mjs` 用它们读一份流程:方言里没有的、值不合 schema 的都拒绝,说法是「位置:schema 对那里的说明」。值的 schema 用清单的那几个关键字,另加三个:`x-feel`(是 FEEL:「=」开头是表达式,否则文字里的 `{{ … }}` 是,按这份流程里的名字读得通)、`x-duration`(ISO 8601 时长,不为负)、`x-number`(文字换算成数字是个有限的数)。`structure.json` 声明流程结构规则,`structure.mjs` 负责跨节点检查。
|
|
8
8
|
- **Agent 接入扩展**(`schemas/agent/`、`agent.mjs`、`docs/agent-protocol.md`):Coda 怎样用 ACP 接 Agent,以及 Coda 加的扩展(会话指令、插话、`_coda/wait`、执行失败、工具调用的审核)。
|
|
@@ -30,7 +30,7 @@ import { manifestProblem, pointProblem, valueProblem, TYPES, SCHEMAS } from '@co
|
|
|
30
30
|
|
|
31
31
|
// 整份清单;给出插件目录时,连同它点名的文件一起查(x-file)。合乎类型返回 null,否则是一句说明:哪里、schema 怎么说。
|
|
32
32
|
const problem = manifestProblem(JSON.parse(readFileSync('plugin/package.json', 'utf8')), 'plugin');
|
|
33
|
-
//
|
|
33
|
+
// 某个类型里的一部分,如输入框模式声明的参数。
|
|
34
34
|
valueProblem('composer-modes.json#/$defs/parameters', parameters, '插件给出的参数');
|
|
35
35
|
// 写给别的插件扩展点的东西,按那个扩展点的 schema(已读出的对象);给出写它的插件目录时连同它点名的文件一起查。
|
|
36
36
|
pointProblem(panelsSchema, manifest.contributes.panels, 'contributes.panels', 'plugin');
|
|
@@ -63,6 +63,14 @@ fieldsProblem(JSON.parse(fieldsJSON), 'ask');
|
|
|
63
63
|
|
|
64
64
|
设置的 `items.anyOf` 选项可附带 `workflowKind`(`check`、`premerge`、`deploy`、`preview`、`release`)。全局设置显示全部选项;项目设置只显示该项目有的流程类别,未写此字段的选项始终显示。宿主负责按项目提供可选项,流程执行时仍需按运行的提交判断可用操作。
|
|
65
65
|
|
|
66
|
+
## 输入框模式
|
|
67
|
+
|
|
68
|
+
从 2.0.0 起,`contributes.composerModes` 可写 `once: true`:这个模式里发出的一条被收下后,它从输入框退出。`once` 是可选布尔值。模式不再接受 `agent`,工具栏始终使用输入框自己的执行者;旧清单须删除模式级的 `agent` 字段。
|
|
69
|
+
|
|
70
|
+
模式的对象数组参数(`type: array` 且 `items.type: object`)可写 `"presets": { "from": "composer.templates", "by": "template" }`。`from` 是表格设置 ID,`by` 是分组列,两者都是必填字符串,不接受其他字段。此字段只用于模式参数,不用于普通设置。Coda 按输入框所在目录读取设置,以分组列首次出现的顺序显示预设;选中时一次性填入该组的行,只取项中声明的列,之后可普通编辑。进入模式时填入第一组,设置为空则不填。
|
|
71
|
+
|
|
72
|
+
插件 schema 的 `$id` 命名空间随此不兼容变更升为 `https://coda.pub/schemas/plugin/v2/`;直接写绝对 `$ref` 的调用方须同步更新。Agent 接入和流程方言的 schema 命名空间不变。
|
|
73
|
+
|
|
66
74
|
## Agent 接入扩展
|
|
67
75
|
|
|
68
76
|
从 1.9.0 起,[Agent 协议正文](docs/agent-protocol.md#可执行的形状定义)引用 `schemas/agent/` 的可执行定义:
|
|
@@ -88,6 +96,15 @@ agentValueProblem('review.json', review, '工具的审核');
|
|
|
88
96
|
深层/大型 JSON 用显式栈检查,非有限数字与循环引用不会通过。会话归属、活动状态迁移、调用起因、协商、超时、重试和授权仍由宿主判断;
|
|
89
97
|
丢弃未知风险值、压缩答复的裁剪与兜底等整理也留在 Coda。详见正文的兼容性与接入说明。
|
|
90
98
|
|
|
99
|
+
## 运行器与执行请求
|
|
100
|
+
|
|
101
|
+
从 2.1.0 起,`executor` 与 `execution` 是 Coda 预定公共对象协议,见[运行器协议正文](docs/executor-protocol.md)。
|
|
102
|
+
`schemas/executor/v1/` 使用默认 Ajv 可读的 draft-07;`@coda-console/protocol/executor` 导出
|
|
103
|
+
`EXECUTOR_PROTOCOL`、`EXECUTION_PROTOCOL`,其 spec/status 都自包含,可直接传给 `protocols.define`。
|
|
104
|
+
首版请求仅支持 `{slots: 1}`,可独占 slots;不支持多单位或多资源。两种类型均无补充栏,execution 绑定是由调度办事员 CAS 更新的单值。
|
|
105
|
+
executor 无 target,status 由持有端写;execution 绑定后由执行方完整回传,开跑前须经本机账本准入。
|
|
106
|
+
对象协议 v1 与 npm 包版本、插件清单 v2 独立;旧 `contributes.executors` 不变。
|
|
107
|
+
|
|
91
108
|
## 谁在用
|
|
92
109
|
|
|
93
110
|
- **Coda**:读插件时用它校验清单,再整理成自己的结构;只另看插件身份(ID、设置名在 ID 之下),以及装到一台机器上才知道的(来源、已装插件的冲突)。读流程时用 `readWorkflow` 同时校验方言、值与结构,再编译成引擎需要的数据。
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# 运行器与执行请求协议 v1
|
|
2
|
+
|
|
3
|
+
`executor`(运行器)与 `execution`(一次执行尝试)是 Coda 预定的公共协议,唯一形状定义在
|
|
4
|
+
`@coda-console/protocol`。依据是 [coda#2078 已验收草案](https://github.com/coda-console/coda/issues/2078#issuecomment-6554881338),
|
|
5
|
+
首版按验收决定收窄为 **单资源 `slots`,每请求 `slots: 1`,可独占 `slots`**。多单位、多资源须等本机账本支持后再开放。
|
|
6
|
+
|
|
7
|
+
协议对象版本 `protocolVersion: 1`、本目录的 v1、npm 包版本 2.1.0 和插件清单的 v2 分开演进。
|
|
8
|
+
本次只增加契约;不改变 `contributes.executors`,也不宣称 host、scheduler、空间引用解析或账本接口已经实现。
|
|
9
|
+
|
|
10
|
+
## 定义与登记
|
|
11
|
+
|
|
12
|
+
`schemas/executor/v1/{executor,execution}.json` 分别描述 `{spec,status}`,公共定义在 `common.json`。
|
|
13
|
+
原始 JSON 采用 draft-07,读取原文件时先向 Ajv 登记 `common.json`。模块导出把所需定义包含在每份 spec/status 中,
|
|
14
|
+
只留本地 `$ref`;四份 schema 均可由一个全新的默认 Ajv 直接编译,不需自定义关键字、format 或外部 schema 注册。
|
|
15
|
+
`spec`、`status` 顶层均为 object。不支持的 `protocolVersion`、未知字段与未知资源键明确拒绝。
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
import { EXECUTOR_PROTOCOL, EXECUTION_PROTOCOL } from '@coda-console/protocol/executor';
|
|
19
|
+
|
|
20
|
+
// 现行 API 的装配示例:由 host 在提交端和执行端登记,提交端可关闭接活。
|
|
21
|
+
await coda.protocols.define(EXECUTOR_PROTOCOL);
|
|
22
|
+
await coda.protocols.define({ ...EXECUTION_PROTOCOL, controllers: ['scheduler'] });
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`EXECUTOR_PROTOCOL` 没有 `target`;`EXECUTION_PROTOCOL.target` 为 `executor`。
|
|
26
|
+
两种类型均**没有补充栏**,不声明 append/or 合并;任务、标签、取消和绑定均不经补充者修改。
|
|
27
|
+
`controllers: ['scheduler']` 是现行接口的插件 ID 装配,不是协议对 host/scheduler 实现路径的依赖。
|
|
28
|
+
其他生产者通过 provider 的本机创建/取消入口操作,不能冒用 `put`。
|
|
29
|
+
|
|
30
|
+
当前 `protocols.define` 只允许一个插件登记同 kind,定义持久化后不能改变;尚不支持按协议依赖、多实现登记或同 kind 版本迁移。
|
|
31
|
+
后续替换 provider 或演进声明须先解决 Coda 通用登记机制,不能仅更换 schema 文件接管既存类型。
|
|
32
|
+
|
|
33
|
+
信封沿用 `{kind,id,instance,spec,status,revision}`。基座维护 `instance` 与不透明 `revision`;插件原样传回观察到的 revision,
|
|
34
|
+
不能解析或自行递增。删除后同 id 重建是新 instance,旧回答不能复用。这里的 schema 校验 spec/status,不校验或重定义信封。
|
|
35
|
+
所有 status 都允许 `{}`,表示镜像状态尚未到达,**不能据此判定未开始**;非空 status 必须是一份完整的已知状态。
|
|
36
|
+
|
|
37
|
+
## executor:配置与本机事实
|
|
38
|
+
|
|
39
|
+
一个对象对应一个后端实例的执行环境,身份不随显示名或插件版本改变。
|
|
40
|
+
|
|
41
|
+
| 区域 | 字段 |
|
|
42
|
+
| --- | --- |
|
|
43
|
+
| spec 必填 | `protocolVersion: 1`、`enabled`、`capacity: {slots: 非负整数}`、`labels: string[]` |
|
|
44
|
+
| spec 可选 | `title`(不超过 200 字符) |
|
|
45
|
+
| status 能力 | `detectedLabels`、`capabilities: {os,arch,nodeVersion,graphicalSession}`;可选 `xcode: {version,build}`、`simulatorRuntimes: [{id,version,available}]` |
|
|
46
|
+
| status 资源 | `capacity`、`occupied`、`available`,各为 `{slots: 非负整数}`;`accepting` |
|
|
47
|
+
| status 观察 | `observedAt`、`observedRevision`;可选 `reason: {code,message}` |
|
|
48
|
+
| status 支持 | `executionFeatures: {multiResource: false,multiUnit: false,exclusive: boolean}` |
|
|
49
|
+
|
|
50
|
+
非空 status 除明确可选项外全部必填。`graphicalSession` 是 boolean 或 null;null 表示无法确定。
|
|
51
|
+
探测失败写 `reason`,不将失败变成用户标签或伪造能力;可选探测项未知时省略。
|
|
52
|
+
探测只读,不启动模拟器。`observedRevision` 对应探测时采用的配置版本,而非要求它等于之后每次 status 写入产生的 revision。
|
|
53
|
+
|
|
54
|
+
标签使用小写字母、数字、`.`、`_`、`-`,以字母或数字起始,每项最多 40 字符、每列表最多 64 项、不重复。
|
|
55
|
+
`requirements.labels` 沿用 runsOn 的**全部满足**语义,匹配用户标签与探测标签的并集。
|
|
56
|
+
保留 OS 标签 `darwin/linux/win32/aix/android/freebsd/openbsd/sunos/cygwin/netbsd/haiku`、架构标签
|
|
57
|
+
`arm/arm64/ia32/x64/loong64/mips/mipsel/ppc/ppc64/riscv64/s390/s390x`、`gui`,以及 `xcode-`、`ios-`、`node-` 前缀给探测结果。
|
|
58
|
+
spec.labels 只能加自定义标签,不能写这些保留值。精确版本和 simulator runtime ID 另外保存在结构化能力中。
|
|
59
|
+
|
|
60
|
+
`slots` 的单位是执行名额,host 映射到同一本机 `host` 账本;scheduler 比较资源键与数量,不硬编码 `group === 'host'`。
|
|
61
|
+
容量可大于 1;**每次请求量仍只能为 1**。其他资源键拒绝,不能按 0 算。有能力不代表有名额。
|
|
62
|
+
缩容量允许 occupied 暂时大于 capacity,此时 available 归零并停止新准入,不强杀已有任务。
|
|
63
|
+
占用含旧流程与 run-held;借用或独占按账本事实计算,不能简单数 host 自己启动的进程,也不能要求独占时 available 等于 capacity − occupied。
|
|
64
|
+
这些跨栏计算和准入状态由实现检查,不加动态 schema 关键字。
|
|
65
|
+
|
|
66
|
+
对象无 target,配置经运行器持有端更新;**status 只由持有后端的实现者写**,空间层单向复制。
|
|
67
|
+
accepting、available、observedAt 都只是最近观察事实,不保证在线,也不是预留凭证;新鲜度阈值与选择策略属于 scheduler。
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"spec": {
|
|
72
|
+
"protocolVersion": 1,
|
|
73
|
+
"enabled": true,
|
|
74
|
+
"capacity": {"slots": 2},
|
|
75
|
+
"labels": ["build-lab"],
|
|
76
|
+
"title": "Mac mini"
|
|
77
|
+
},
|
|
78
|
+
"status": {
|
|
79
|
+
"detectedLabels": ["darwin", "arm64", "xcode-26.1", "node-26"],
|
|
80
|
+
"capabilities": {"os": "darwin", "arch": "arm64", "nodeVersion": "26.8.2", "graphicalSession": null, "xcode": {"version": "26.1", "build": "17B55"}},
|
|
81
|
+
"capacity": {"slots": 2},
|
|
82
|
+
"occupied": {"slots": 1},
|
|
83
|
+
"available": {"slots": 1},
|
|
84
|
+
"accepting": true,
|
|
85
|
+
"observedAt": "2026-11-01T07:00:00Z",
|
|
86
|
+
"observedRevision": "opaque-config-revision",
|
|
87
|
+
"executionFeatures": {"multiResource": false, "multiUnit": false, "exclusive": true}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## execution:一次尝试
|
|
93
|
+
|
|
94
|
+
正本在发起后端。`run: ResourceRef` 关联已有运行对象,`step?` 关联步骤,`attempt` 是正整数。
|
|
95
|
+
重新执行创建新 execution ID 和新 attempt,保留上一尝试,不另造 Run 或会话命令队列。取消不是新尝试。
|
|
96
|
+
|
|
97
|
+
| spec 区域 | 形状与约束 |
|
|
98
|
+
| --- | --- |
|
|
99
|
+
| 身份 | `protocolVersion: 1`、`run`、`attempt` 必填;`step` 可选 |
|
|
100
|
+
| 源码 | `source` 必填:null、`{snapshot: ResourceRef<sourceSnapshot>}`、`{repository: ResourceRef<gitSource>,commit: 完整 SHA}` 三选一;SHA 为 40 或 64 位十六进制 |
|
|
101
|
+
| 能力 | `requirements: {labels: string[]}`,可为空列表 |
|
|
102
|
+
| 资源 | `resources: {slots: 1}`;`exclusiveResources` 默认 `[]`,仅可为 `[]` 或 `["slots"]` |
|
|
103
|
+
| 任务 | `{script,shell}` 或 `{command,args}`,严格二选一;程序名与参数不隐含 shell |
|
|
104
|
+
| 环境 | `workingDirectory` 默认 `.`;`env: Record<string,string>` 必填,只含声明变量,可为空对象 |
|
|
105
|
+
| 时限 | `timeoutMs?` 正整数;`cancelGraceMs?` 非负整数;缺省策略由执行实现决定 |
|
|
106
|
+
| 取消 | `cancel` 默认 false,是单向意图 |
|
|
107
|
+
| 绑定 | `executor: ResourceRef<executor>\|null`、`assignmentId: string\|null` 必填,两者同为 null 或同时有值 |
|
|
108
|
+
|
|
109
|
+
独占是 `exclusiveResources: ["slots"]`,而非把“全部名额”换成某次观察到的容量数字。
|
|
110
|
+
生产端必须先看 `executionFeatures.exclusive`;不支持独占的执行方明确拒绝,不降级为普通名额。
|
|
111
|
+
原 coda:group 由生产端/资源 provider 声明映射;runsOn 放入 labels,两者独立。
|
|
112
|
+
|
|
113
|
+
workingDirectory 是源码根下的相对路径;没有源码时相对执行方隔离目录。
|
|
114
|
+
schema 拒绝绝对路径、盘符、反斜线、控制字符和 `..` 路径段;执行方仍须检查真实落点、符号链接和来源权限。
|
|
115
|
+
工作树先由提交端制成 ready 快照,执行端读取快照的精确 commit/ref,不重新抓取“最新工作树”。
|
|
116
|
+
env 不等于提交机进程环境;沿用 hook 的声明变量、密文与鉴权边界,不把本机凭据或绝对路径放进跨机 spec。
|
|
117
|
+
|
|
118
|
+
JSON Schema 的 default 只声明缺省值。单独使用默认 Ajv 校验不会填值;现行 Provider 用 `useDefaults` 补齐
|
|
119
|
+
`workingDirectory`、`exclusiveResources`、`cancel`。不使用 default 填造任何 status 事实。
|
|
120
|
+
|
|
121
|
+
| 非空 status | 约束 |
|
|
122
|
+
| --- | --- |
|
|
123
|
+
| 基本 | `phase: queued\|preparing\|running\|finished` 与 `assignmentId` 必填;可选 `reason` |
|
|
124
|
+
| 进度 | 可选 `startedAt`、`executionRun: ResourceRef`、`admission: {lease,slots}`;running 三者必填 |
|
|
125
|
+
| 准入 | lease 是不透明本机凭证,slots 是不重复的非负名额序号数组;独占可记录多个序号,仅表示事实 |
|
|
126
|
+
| 完成 | finished 必须给 `finishedAt`、`outcome: succeeded\|failed\|cancelled\|rejected`;其他 phase 不接受 finishedAt、outcome、exitCode |
|
|
127
|
+
| 结果 | 可选 `exitCode: integer\|null`、`outputs`、`summary`、`outputTail`、`logs`、`artifacts` |
|
|
128
|
+
|
|
129
|
+
preparing/running 必须有 assignmentId;succeeded/failed 还必须有 startedAt 与 assignmentId。
|
|
130
|
+
取消可在未绑定、未启动时完成;rejected 表示本次 assignment 不会启动,不接受 startedAt 或非 null exitCode。
|
|
131
|
+
没启动或无法确定退出码时省略或写 null,不能造 0。这些字段的静态组合不能代替实际进程、收尾和状态迁移检查。
|
|
132
|
+
|
|
133
|
+
文本限制使用 JSON Schema 的 Unicode 字符数,不是 UTF-8 字节或 UTF-16 长度:summary 最多 4096、outputTail 最多 16384;
|
|
134
|
+
outputs 最多 64 项,键 1–128 字符、值最多 4096。logs/artifacts 各最多 64 项。
|
|
135
|
+
logs 项为 `{ref: ResourceRef,cursor?}`;artifacts 项为 `{ref: ResourceRef,name,sha256?,size?,mediaType?}`。
|
|
136
|
+
引用不接受本机绝对路径代替对象;sha256 为 64 位十六进制,size 为非负安全整数。
|
|
137
|
+
任务脚本最多 65536 字符,command/shell 最多 4096,args 最多 256 项、每项最多 16384;
|
|
138
|
+
env 最多 128 项,变量名使用字母/下划线起始的常规环境变量名、最多 128 字符,值最多 16384。
|
|
139
|
+
数组、键和值都有上限,不能把无限日志复制进 status;完整限制见 schema。
|
|
140
|
+
时间统一使用 UTC `...Z`,schema 仅检查日期时间文字的形状,写入方保证日期真实、时序合理。
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"spec": {
|
|
145
|
+
"protocolVersion": 1,
|
|
146
|
+
"run": {"kind": "runs", "id": "opaque-run-ref"},
|
|
147
|
+
"step": "check",
|
|
148
|
+
"attempt": 1,
|
|
149
|
+
"source": {"snapshot": {"kind": "sourceSnapshot", "id": "opaque-snapshot-ref"}},
|
|
150
|
+
"requirements": {"labels": ["darwin", "arm64"]},
|
|
151
|
+
"resources": {"slots": 1},
|
|
152
|
+
"exclusiveResources": ["slots"],
|
|
153
|
+
"task": {"command": "npm", "args": ["test"]},
|
|
154
|
+
"workingDirectory": ".",
|
|
155
|
+
"env": {"CI": "true"},
|
|
156
|
+
"cancel": false,
|
|
157
|
+
"executor": {"kind": "executor", "id": "opaque-executor-ref"},
|
|
158
|
+
"assignmentId": "assignment-1"
|
|
159
|
+
},
|
|
160
|
+
"status": {
|
|
161
|
+
"phase": "finished",
|
|
162
|
+
"assignmentId": "assignment-1",
|
|
163
|
+
"startedAt": "2026-11-01T07:00:00Z",
|
|
164
|
+
"finishedAt": "2026-11-01T07:01:00Z",
|
|
165
|
+
"executionRun": {"kind": "runs", "id": "opaque-execution-run-ref"},
|
|
166
|
+
"admission": {"lease": "opaque-lease", "slots": [0, 1]},
|
|
167
|
+
"outcome": "succeeded",
|
|
168
|
+
"exitCode": 0,
|
|
169
|
+
"summary": "检查通过",
|
|
170
|
+
"logs": [{"ref": {"kind": "logs", "id": "opaque-log-ref"}}]
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
ResourceRef 的插件可见形状严格为 `{kind,id}`,id 是适配层提供的不透明引用。
|
|
176
|
+
示例里的 `runs`、`logs` 是引用示意,不在本协议登记新类型;实际使用现有运行和日志对象的 kind。
|
|
177
|
+
适配层必须保持源码、运行、日志和产物的持有者归属,不能把本机 `remote:…` 字符串原样传给另一台便认为能解析。
|
|
178
|
+
日志衔接现有运行日志读面;产物这里只预留引用、大小与摘要,字节传输和 sha256 核验仍属后续 artifacts 实施步骤。
|
|
179
|
+
|
|
180
|
+
## 写权限、绑定与改派
|
|
181
|
+
|
|
182
|
+
1. executor 无 target,status 权威是持有端。execution 未绑定时,本机 provider/获准 scheduler 可写 queued 与原因;
|
|
183
|
+
创建时可给 queued,缺失状态是未知。scheduler 只处理本机正本,按观察到的 revision 同次 CAS 写入 executor 与新的 assignmentId。
|
|
184
|
+
绑定是顶层单值,**不走 append/or 补充合并**,不将多个候选合并成绑定。
|
|
185
|
+
2. 绑定后任务、源码、身份、资源与需求冻结,只可单向设置 cancel;安全改派仅更换绑定两栏。
|
|
186
|
+
空间桥将 executor 翻译成 `coda.pub/target`。执行方只观察 assigned,并核对具体 executor ID 是自己实现的;
|
|
187
|
+
指向后端不表示该后端所有执行环境都可领单。**绑定后只有执行方完整回传 status**,包括字段删除;
|
|
188
|
+
其他节点无权写,持有者不能伪造远端完成。旧 status 按 assignmentId 区分,不能拿旧绑定结果解释新绑定。
|
|
189
|
+
3. 未绑定请求可直接选择。已绑定请求只有当前 assignment 明确拒绝,或撤销已确认且证明不会再启动,才可改派。
|
|
190
|
+
对齐 assignmentId、验证拒绝/撤销事实和权限是机制责任,单凭符合 schema 的对象不能授权改派。
|
|
191
|
+
旧 queued、缺 startedAt、空 status、租约超时或节点掉线均不能证明未开始;已开始或是否开始不明,保留原绑定与运行引用并恢复观察。
|
|
192
|
+
4. 持有端将不可达作为观察状态展示,不代写执行结果。取消或撤下镜像也不是原进程已停证明。
|
|
193
|
+
自动重试依任务幂等性和重试政策,创建新 attempt;协议不承诺网络分区中的 exactly-once。
|
|
194
|
+
|
|
195
|
+
## 开跑前的本机账本准入与恢复
|
|
196
|
+
|
|
197
|
+
执行方须在 prepare/启动前,持久登记 **execution 的 instance + assignmentId**,经同一本机账本原子获得资源与固定名额序号。
|
|
198
|
+
调度看到 available 或 binding 成功均不等于准入。普通请求只占一个 slots;独占须走账本独占动作,不能连续读 available 或分批占额模拟。
|
|
199
|
+
容量冲突可排队或明确拒绝,scheduler 决定是否在满足改派条件后换目标。
|
|
200
|
+
多键/多数量原子准入尚未开放,schema 和 executionFeatures 均不宣称支持。
|
|
201
|
+
|
|
202
|
+
进程和 cleanup 确认结束后才释放;故障期间保留待恢复占用。重启 list-watch 的 ADDED 只能对账,不得再次启动同一 instance + assignment。
|
|
203
|
+
旧镜像指纹/revision 防回写不能代替防重复启动。被拒绝的旧结果不能简单换成最新 revision 重交。
|
|
204
|
+
|
|
205
|
+
`control` 对同对象串行回调;不能 await 整段长任务堵住取消。
|
|
206
|
+
办事员登记并启动/跟随持久运行后返回,后续观察更新取消进程组。取消只改 spec.cancel,执行方重读同 assignment 的当前意图,
|
|
207
|
+
完成取消与收尾后报告 cancelled;取消不能回退为 false,也不能回写旧请求的结果。
|
|
208
|
+
|
|
209
|
+
## 与现有执行链的衔接边界
|
|
210
|
+
|
|
211
|
+
执行保留 `prepare_job / run_script_step / cleanup_job` 三段 hook,以及 `{command,responseFile,args,state}`、准备 state、
|
|
212
|
+
日志、退出码、取消进程组、失败也收尾的语义。清单 `command` 是 hook 入口,与 `task.command` 不同。
|
|
213
|
+
源码 provider 给出精确 commit/ref 与本机鉴权代理入口;hook 本机路径在执行方生成,不进入跨机 spec。
|
|
214
|
+
|
|
215
|
+
缓存目录建议 `$CODA_PLUGIN_STATE/workspaces/<source-key>/<slot-index>/`:source-key 来自来源工作副本身份及对象 instance,
|
|
216
|
+
不能用 origin URL、提交号或快照 ID 代替;同仓库不同 linked worktree 也隔离。
|
|
217
|
+
slot-index 来自本机账本,scheduler 不猜序号;借用也不得使目录重入。日志/响应文件另放尝试目录。
|
|
218
|
+
准入后 fetch 精确 ref/commit、核对实际 commit,再 reset --hard 与 clean -fd,保留忽略缓存;cleanup 清理本次临时资源并放开资源,保留缓存副本。
|
|
219
|
+
发布/部署仍由对应流程和插件检查已合并提交准入,不因支持快照删除 origin/发布检查。
|
|
220
|
+
|
|
221
|
+
旧 Executor 发布与 steps/Step 跨机请求由后续消费方切换撤除,不永久保留两套调度真相。
|
|
222
|
+
本包只交付标准 JSON Schema 和规范;引用可移植解析、本机鉴权入口、账本准入/取消/释放及稳定序号、恢复日志跟随、
|
|
223
|
+
host/scheduler 实现与流程切换均属于各自仓库后续工作。来源权限、时间先后、容量计算、状态迁移和真实路径不伪装成 schema 动态审核器。
|
package/executor.mjs
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Public object contracts. Each spec/status can be compiled by a fresh, default Ajv (draft-07).
|
|
2
|
+
// Only local references remain; no validator keywords, provider identity or scheduling policy are added here.
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
|
|
5
|
+
const base = 'https://coda.pub/schemas/executor/v1/';
|
|
6
|
+
const read = name => JSON.parse(readFileSync(new URL(`./schemas/executor/v1/${name}.json`, import.meta.url), 'utf8'));
|
|
7
|
+
const common = read('common');
|
|
8
|
+
const local = value => JSON.parse(JSON.stringify(value).replaceAll('common.json#/definitions/', '#/definitions/'));
|
|
9
|
+
function protocol(kind) {
|
|
10
|
+
const schema = read(kind);
|
|
11
|
+
const part = name => ({
|
|
12
|
+
$schema: schema.$schema,
|
|
13
|
+
$id: `${base}${kind}.${name}.json`,
|
|
14
|
+
...local(schema.properties[name]),
|
|
15
|
+
definitions: local(common.definitions),
|
|
16
|
+
});
|
|
17
|
+
return { kind, spec: part('spec'), status: part('status') };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const EXECUTOR_PROTOCOL = protocol('executor');
|
|
21
|
+
export const EXECUTION_PROTOCOL = { ...protocol('execution'), target: 'executor' };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coda-console/protocol",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Coda 的协议:插件清单的类型(JSON Schema)、通用校验器、流程方言与 Agent 接入协议",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"agent.mjs",
|
|
18
18
|
"workflow",
|
|
19
19
|
"workflow.mjs",
|
|
20
|
-
"docs"
|
|
20
|
+
"docs",
|
|
21
|
+
"executor.mjs"
|
|
21
22
|
],
|
|
22
23
|
"exports": {
|
|
23
24
|
"./validate": "./validate.mjs",
|
|
@@ -26,7 +27,8 @@
|
|
|
26
27
|
"./workflow/*": "./workflow/*",
|
|
27
28
|
"./docs/*": "./docs/*",
|
|
28
29
|
"./package.json": "./package.json",
|
|
29
|
-
"./agent": "./agent.mjs"
|
|
30
|
+
"./agent": "./agent.mjs",
|
|
31
|
+
"./executor": "./executor.mjs"
|
|
30
32
|
},
|
|
31
33
|
"engines": {
|
|
32
34
|
"node": ">=24"
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://coda.pub/schemas/executor/v1/common.json",
|
|
4
|
+
"title": "运行器协议共享静态定义",
|
|
5
|
+
"definitions": {
|
|
6
|
+
"opaqueId": {"type": "string", "minLength": 1, "maxLength": 2048, "description": "适配层给出的不透明标识;不解析或拼接机器路由"},
|
|
7
|
+
"resourceRef": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"properties": {"kind": {"type": "string", "minLength": 1, "maxLength": 128}, "id": {"$ref": "common.json#/definitions/opaqueId"}},
|
|
10
|
+
"required": ["kind", "id"],
|
|
11
|
+
"additionalProperties": false
|
|
12
|
+
},
|
|
13
|
+
"executorRef": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"properties": {"kind": {"const": "executor"}, "id": {"$ref": "common.json#/definitions/opaqueId"}},
|
|
16
|
+
"required": ["kind", "id"],
|
|
17
|
+
"additionalProperties": false
|
|
18
|
+
},
|
|
19
|
+
"snapshotRef": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"properties": {"kind": {"const": "sourceSnapshot"}, "id": {"$ref": "common.json#/definitions/opaqueId"}},
|
|
22
|
+
"required": ["kind", "id"],
|
|
23
|
+
"additionalProperties": false
|
|
24
|
+
},
|
|
25
|
+
"repositoryRef": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"properties": {"kind": {"const": "gitSource"}, "id": {"$ref": "common.json#/definitions/opaqueId"}},
|
|
28
|
+
"required": ["kind", "id"],
|
|
29
|
+
"additionalProperties": false
|
|
30
|
+
},
|
|
31
|
+
"labels": {
|
|
32
|
+
"type": "array",
|
|
33
|
+
"items": {"type": "string", "minLength": 1, "maxLength": 40, "pattern": "^[a-z0-9][a-z0-9._-]*$(?![\\s\\S])"},
|
|
34
|
+
"maxItems": 64,
|
|
35
|
+
"uniqueItems": true
|
|
36
|
+
},
|
|
37
|
+
"userLabels": {
|
|
38
|
+
"type": "array",
|
|
39
|
+
"items": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"minLength": 1,
|
|
42
|
+
"maxLength": 40,
|
|
43
|
+
"pattern": "^[a-z0-9][a-z0-9._-]*$(?![\\s\\S])",
|
|
44
|
+
"not": {
|
|
45
|
+
"pattern": "^(darwin|linux|win32|aix|android|freebsd|openbsd|sunos|cygwin|netbsd|haiku|arm|arm64|ia32|x64|loong64|mips|mipsel|ppc|ppc64|riscv64|s390|s390x|gui)$|^(xcode|ios|node)-"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"maxItems": 64,
|
|
49
|
+
"uniqueItems": true,
|
|
50
|
+
"description": "用户自定义标签,不占用 OS、架构、gui 与 xcode-/ios-/node- 探测命名空间"
|
|
51
|
+
},
|
|
52
|
+
"resourceAmounts": {
|
|
53
|
+
"type": "object",
|
|
54
|
+
"properties": {"slots": {"type": "integer", "minimum": 0, "maximum": 9007199254740991}},
|
|
55
|
+
"required": ["slots"],
|
|
56
|
+
"additionalProperties": false,
|
|
57
|
+
"description": "首版只有 slots;容量与占用可大于 1,请求量另限为 1"
|
|
58
|
+
},
|
|
59
|
+
"reason": {
|
|
60
|
+
"type": "object",
|
|
61
|
+
"properties": {
|
|
62
|
+
"code": {"type": "string", "minLength": 1, "maxLength": 128},
|
|
63
|
+
"message": {"type": "string", "minLength": 1, "maxLength": 4096}
|
|
64
|
+
},
|
|
65
|
+
"required": ["code", "message"],
|
|
66
|
+
"additionalProperties": false
|
|
67
|
+
},
|
|
68
|
+
"timestamp": {
|
|
69
|
+
"type": "string",
|
|
70
|
+
"minLength": 1,
|
|
71
|
+
"maxLength": 40,
|
|
72
|
+
"pattern": "^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])T([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(\\.\\d{1,9})?Z$(?![\\s\\S])",
|
|
73
|
+
"description": "UTC 时间文字;schema 检查形状,真实日历时间由写入方保证"
|
|
74
|
+
},
|
|
75
|
+
"assignmentId": {"type": "string", "minLength": 1, "maxLength": 256, "description": "每次绑定生成的新 token,与 executor 同次 CAS 更新"},
|
|
76
|
+
"workingDirectory": {
|
|
77
|
+
"type": "string",
|
|
78
|
+
"minLength": 1,
|
|
79
|
+
"maxLength": 4096,
|
|
80
|
+
"pattern": "^(?!/)(?![A-Za-z]:)(?!.*\\\\)(?!.*[\\u0000-\\u001f\\u007f])(?!.*(?:^|/)\\.\\.(?:/|$)).+$",
|
|
81
|
+
"description": "工作根下的相对路径;拒绝绝对路径、反斜线和 .. 段,真实落点与符号链接仍须执行方检查"
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://coda.pub/schemas/executor/v1/execution.json",
|
|
4
|
+
"title": "execution:一次执行尝试的请求与完整结果",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"spec": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"properties": {
|
|
10
|
+
"protocolVersion": {"const": 1},
|
|
11
|
+
"run": {"$ref": "common.json#/definitions/resourceRef"},
|
|
12
|
+
"step": {"type": "string", "minLength": 1, "maxLength": 256},
|
|
13
|
+
"attempt": {"type": "integer", "minimum": 1, "maximum": 9007199254740991},
|
|
14
|
+
"source": {
|
|
15
|
+
"oneOf": [
|
|
16
|
+
{"type": "null"},
|
|
17
|
+
{
|
|
18
|
+
"type": "object",
|
|
19
|
+
"properties": {"snapshot": {"$ref": "common.json#/definitions/snapshotRef"}},
|
|
20
|
+
"required": ["snapshot"],
|
|
21
|
+
"additionalProperties": false
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"type": "object",
|
|
25
|
+
"properties": {
|
|
26
|
+
"repository": {"$ref": "common.json#/definitions/repositoryRef"},
|
|
27
|
+
"commit": {"type": "string", "minLength": 1, "maxLength": 64, "pattern": "^(?:[0-9a-fA-F]{40}|[0-9a-fA-F]{64})$(?![\\s\\S])"}
|
|
28
|
+
},
|
|
29
|
+
"required": ["repository", "commit"],
|
|
30
|
+
"additionalProperties": false
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
"requirements": {
|
|
35
|
+
"type": "object",
|
|
36
|
+
"properties": {"labels": {"$ref": "common.json#/definitions/labels"}},
|
|
37
|
+
"required": ["labels"],
|
|
38
|
+
"additionalProperties": false
|
|
39
|
+
},
|
|
40
|
+
"resources": {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"properties": {"slots": {"const": 1}},
|
|
43
|
+
"required": ["slots"],
|
|
44
|
+
"additionalProperties": false,
|
|
45
|
+
"description": "首版仅 slots: 1;不接受多单位或其他资源"
|
|
46
|
+
},
|
|
47
|
+
"exclusiveResources": {
|
|
48
|
+
"type": "array",
|
|
49
|
+
"items": {"const": "slots"},
|
|
50
|
+
"maxItems": 1,
|
|
51
|
+
"uniqueItems": true,
|
|
52
|
+
"default": [],
|
|
53
|
+
"description": "空数组或独占 slots;不换算成观察到的容量数字"
|
|
54
|
+
},
|
|
55
|
+
"task": {
|
|
56
|
+
"oneOf": [
|
|
57
|
+
{
|
|
58
|
+
"type": "object",
|
|
59
|
+
"properties": {
|
|
60
|
+
"script": {"type": "string", "minLength": 1, "maxLength": 65536},
|
|
61
|
+
"shell": {"type": "string", "minLength": 1, "maxLength": 4096}
|
|
62
|
+
},
|
|
63
|
+
"required": ["script", "shell"],
|
|
64
|
+
"additionalProperties": false
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"type": "object",
|
|
68
|
+
"properties": {
|
|
69
|
+
"command": {"type": "string", "minLength": 1, "maxLength": 4096},
|
|
70
|
+
"args": {"type": "array", "items": {"type": "string", "minLength": 0, "maxLength": 16384}, "maxItems": 256}
|
|
71
|
+
},
|
|
72
|
+
"required": ["command", "args"],
|
|
73
|
+
"additionalProperties": false
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
"workingDirectory": {"$ref": "common.json#/definitions/workingDirectory", "default": "."},
|
|
78
|
+
"env": {
|
|
79
|
+
"type": "object",
|
|
80
|
+
"propertyNames": {"pattern": "^[A-Za-z_][A-Za-z0-9_]{0,127}$(?![\\s\\S])"},
|
|
81
|
+
"maxProperties": 128,
|
|
82
|
+
"additionalProperties": {"type": "string", "minLength": 0, "maxLength": 16384}
|
|
83
|
+
},
|
|
84
|
+
"timeoutMs": {"type": "integer", "minimum": 1, "maximum": 9007199254740991},
|
|
85
|
+
"cancelGraceMs": {"type": "integer", "minimum": 0, "maximum": 9007199254740991},
|
|
86
|
+
"cancel": {"type": "boolean", "default": false},
|
|
87
|
+
"executor": {
|
|
88
|
+
"oneOf": [{"type": "null"}, {"$ref": "common.json#/definitions/executorRef"}],
|
|
89
|
+
"description": "scheduler 写入的顶层单值绑定,与 assignmentId 同次 CAS 更新,不参与补充合并"
|
|
90
|
+
},
|
|
91
|
+
"assignmentId": {"oneOf": [{"type": "null"}, {"$ref": "common.json#/definitions/assignmentId"}]}
|
|
92
|
+
},
|
|
93
|
+
"required": ["protocolVersion", "run", "attempt", "source", "requirements", "resources", "task", "env", "executor", "assignmentId"],
|
|
94
|
+
"additionalProperties": false,
|
|
95
|
+
"allOf": [
|
|
96
|
+
{
|
|
97
|
+
"if": {"properties": {"executor": {"type": "null"}}},
|
|
98
|
+
"then": {"properties": {"assignmentId": {"type": "null"}}},
|
|
99
|
+
"else": {"properties": {"assignmentId": {"$ref": "common.json#/definitions/assignmentId"}}}
|
|
100
|
+
}
|
|
101
|
+
],
|
|
102
|
+
"description": "一次执行尝试;绑定后任务、源码和需求冻结,取消为单向意图"
|
|
103
|
+
},
|
|
104
|
+
"status": {
|
|
105
|
+
"type": "object",
|
|
106
|
+
"properties": {
|
|
107
|
+
"phase": {"enum": ["queued", "preparing", "running", "finished"]},
|
|
108
|
+
"reason": {"$ref": "common.json#/definitions/reason"},
|
|
109
|
+
"assignmentId": {"oneOf": [{"type": "null"}, {"$ref": "common.json#/definitions/assignmentId"}]},
|
|
110
|
+
"startedAt": {"$ref": "common.json#/definitions/timestamp"},
|
|
111
|
+
"finishedAt": {"$ref": "common.json#/definitions/timestamp"},
|
|
112
|
+
"executionRun": {"$ref": "common.json#/definitions/resourceRef"},
|
|
113
|
+
"admission": {
|
|
114
|
+
"type": "object",
|
|
115
|
+
"properties": {
|
|
116
|
+
"lease": {"$ref": "common.json#/definitions/opaqueId"},
|
|
117
|
+
"slots": {
|
|
118
|
+
"type": "array",
|
|
119
|
+
"items": {"type": "integer", "minimum": 0, "maximum": 9007199254740991},
|
|
120
|
+
"maxItems": 4096,
|
|
121
|
+
"minItems": 1,
|
|
122
|
+
"uniqueItems": true
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
"required": ["lease", "slots"],
|
|
126
|
+
"additionalProperties": false
|
|
127
|
+
},
|
|
128
|
+
"outcome": {"enum": ["succeeded", "failed", "cancelled", "rejected"]},
|
|
129
|
+
"exitCode": {"type": ["integer", "null"]},
|
|
130
|
+
"outputs": {
|
|
131
|
+
"type": "object",
|
|
132
|
+
"maxProperties": 64,
|
|
133
|
+
"propertyNames": {"type": "string", "minLength": 1, "maxLength": 128},
|
|
134
|
+
"additionalProperties": {"type": "string", "minLength": 0, "maxLength": 4096}
|
|
135
|
+
},
|
|
136
|
+
"summary": {"type": "string", "minLength": 0, "maxLength": 4096},
|
|
137
|
+
"outputTail": {"type": "string", "minLength": 0, "maxLength": 16384},
|
|
138
|
+
"logs": {
|
|
139
|
+
"type": "array",
|
|
140
|
+
"items": {
|
|
141
|
+
"type": "object",
|
|
142
|
+
"properties": {
|
|
143
|
+
"ref": {"$ref": "common.json#/definitions/resourceRef"},
|
|
144
|
+
"cursor": {"type": "string", "minLength": 1, "maxLength": 2048}
|
|
145
|
+
},
|
|
146
|
+
"required": ["ref"],
|
|
147
|
+
"additionalProperties": false
|
|
148
|
+
},
|
|
149
|
+
"maxItems": 64
|
|
150
|
+
},
|
|
151
|
+
"artifacts": {
|
|
152
|
+
"type": "array",
|
|
153
|
+
"items": {
|
|
154
|
+
"type": "object",
|
|
155
|
+
"properties": {
|
|
156
|
+
"ref": {"$ref": "common.json#/definitions/resourceRef"},
|
|
157
|
+
"name": {"type": "string", "minLength": 1, "maxLength": 256},
|
|
158
|
+
"sha256": {"type": "string", "minLength": 1, "maxLength": 64, "pattern": "^[0-9a-fA-F]{64}$"},
|
|
159
|
+
"size": {"type": "integer", "minimum": 0, "maximum": 9007199254740991},
|
|
160
|
+
"mediaType": {"type": "string", "minLength": 1, "maxLength": 256}
|
|
161
|
+
},
|
|
162
|
+
"required": ["ref", "name"],
|
|
163
|
+
"additionalProperties": false
|
|
164
|
+
},
|
|
165
|
+
"maxItems": 64
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
"required": [],
|
|
169
|
+
"additionalProperties": false,
|
|
170
|
+
"anyOf": [{"maxProperties": 0}, {"required": ["phase", "assignmentId"]}],
|
|
171
|
+
"allOf": [
|
|
172
|
+
{
|
|
173
|
+
"if": {"properties": {"phase": {"const": "finished"}}, "required": ["phase"]},
|
|
174
|
+
"then": {"required": ["outcome", "finishedAt"]},
|
|
175
|
+
"else": {"not": {"anyOf": [{"required": ["outcome"]}, {"required": ["exitCode"]}, {"required": ["finishedAt"]}]}}
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"if": {"properties": {"phase": {"enum": ["preparing", "running"]}}, "required": ["phase"]},
|
|
179
|
+
"then": {"properties": {"assignmentId": {"$ref": "common.json#/definitions/assignmentId"}}}
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"if": {"properties": {"phase": {"const": "running"}}, "required": ["phase"]},
|
|
183
|
+
"then": {"required": ["startedAt", "executionRun", "admission"]}
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"if": {"properties": {"outcome": {"enum": ["succeeded", "failed"]}}, "required": ["outcome"]},
|
|
187
|
+
"then": {"properties": {"assignmentId": {"$ref": "common.json#/definitions/assignmentId"}}, "required": ["startedAt"]}
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"if": {"properties": {"outcome": {"const": "rejected"}}, "required": ["outcome"]},
|
|
191
|
+
"then": {"not": {"required": ["startedAt"]}, "properties": {"exitCode": {"type": "null"}}}
|
|
192
|
+
}
|
|
193
|
+
],
|
|
194
|
+
"description": "空对象表示未知;绑定后由执行方完整回传,按 assignmentId 区分旧回答"
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
"required": ["spec", "status"],
|
|
198
|
+
"additionalProperties": false
|
|
199
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://coda.pub/schemas/executor/v1/executor.json",
|
|
4
|
+
"title": "executor:运行器配置意图与持有端事实",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"spec": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"properties": {
|
|
10
|
+
"protocolVersion": {"const": 1},
|
|
11
|
+
"enabled": {"type": "boolean"},
|
|
12
|
+
"capacity": {"$ref": "common.json#/definitions/resourceAmounts"},
|
|
13
|
+
"labels": {"$ref": "common.json#/definitions/userLabels"},
|
|
14
|
+
"title": {"type": "string", "minLength": 1, "maxLength": 200}
|
|
15
|
+
},
|
|
16
|
+
"required": ["protocolVersion", "enabled", "capacity", "labels"],
|
|
17
|
+
"additionalProperties": false,
|
|
18
|
+
"description": "运行器配置意图;无 target,由持有端维护"
|
|
19
|
+
},
|
|
20
|
+
"status": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"properties": {
|
|
23
|
+
"detectedLabels": {"$ref": "common.json#/definitions/labels"},
|
|
24
|
+
"capabilities": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"properties": {
|
|
27
|
+
"os": {"type": "string", "minLength": 1, "maxLength": 40},
|
|
28
|
+
"arch": {"type": "string", "minLength": 1, "maxLength": 40},
|
|
29
|
+
"nodeVersion": {"type": "string", "minLength": 1, "maxLength": 128},
|
|
30
|
+
"xcode": {
|
|
31
|
+
"type": "object",
|
|
32
|
+
"properties": {
|
|
33
|
+
"version": {"type": "string", "minLength": 1, "maxLength": 128},
|
|
34
|
+
"build": {"type": "string", "minLength": 1, "maxLength": 128}
|
|
35
|
+
},
|
|
36
|
+
"required": ["version", "build"],
|
|
37
|
+
"additionalProperties": false
|
|
38
|
+
},
|
|
39
|
+
"simulatorRuntimes": {
|
|
40
|
+
"type": "array",
|
|
41
|
+
"items": {
|
|
42
|
+
"type": "object",
|
|
43
|
+
"properties": {
|
|
44
|
+
"id": {"type": "string", "minLength": 1, "maxLength": 256},
|
|
45
|
+
"version": {"type": "string", "minLength": 1, "maxLength": 128},
|
|
46
|
+
"available": {"type": "boolean"}
|
|
47
|
+
},
|
|
48
|
+
"required": ["id", "version", "available"],
|
|
49
|
+
"additionalProperties": false
|
|
50
|
+
},
|
|
51
|
+
"maxItems": 256
|
|
52
|
+
},
|
|
53
|
+
"graphicalSession": {"type": ["boolean", "null"]}
|
|
54
|
+
},
|
|
55
|
+
"required": ["os", "arch", "nodeVersion", "graphicalSession"],
|
|
56
|
+
"additionalProperties": false
|
|
57
|
+
},
|
|
58
|
+
"capacity": {"$ref": "common.json#/definitions/resourceAmounts"},
|
|
59
|
+
"occupied": {"$ref": "common.json#/definitions/resourceAmounts"},
|
|
60
|
+
"available": {"$ref": "common.json#/definitions/resourceAmounts"},
|
|
61
|
+
"accepting": {"type": "boolean"},
|
|
62
|
+
"reason": {"$ref": "common.json#/definitions/reason"},
|
|
63
|
+
"observedAt": {"$ref": "common.json#/definitions/timestamp"},
|
|
64
|
+
"observedRevision": {"$ref": "common.json#/definitions/opaqueId"},
|
|
65
|
+
"executionFeatures": {
|
|
66
|
+
"type": "object",
|
|
67
|
+
"properties": {"multiResource": {"const": false}, "multiUnit": {"const": false}, "exclusive": {"type": "boolean"}},
|
|
68
|
+
"required": ["multiResource", "multiUnit", "exclusive"],
|
|
69
|
+
"additionalProperties": false
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"required": [],
|
|
73
|
+
"additionalProperties": false,
|
|
74
|
+
"anyOf": [
|
|
75
|
+
{"maxProperties": 0},
|
|
76
|
+
{
|
|
77
|
+
"required": [
|
|
78
|
+
"detectedLabels",
|
|
79
|
+
"capabilities",
|
|
80
|
+
"capacity",
|
|
81
|
+
"occupied",
|
|
82
|
+
"available",
|
|
83
|
+
"accepting",
|
|
84
|
+
"observedAt",
|
|
85
|
+
"observedRevision",
|
|
86
|
+
"executionFeatures"
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
],
|
|
90
|
+
"description": "空对象表示未知;非空为持有端完整事实,不是在线或预留保证"
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"required": ["spec", "status"],
|
|
94
|
+
"additionalProperties": false
|
|
95
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://coda.pub/schemas/plugin/
|
|
3
|
+
"$id": "https://coda.pub/schemas/plugin/v2/agents.json",
|
|
4
4
|
"title": "contributes.agents:插件带来的 Agent",
|
|
5
5
|
"description": "每个是一个讲 ACP(加 Coda 的等待项)的程序:它是谁、客户端怎么画它、Coda 怎么启动它。",
|
|
6
6
|
"type": "array",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://coda.pub/schemas/plugin/
|
|
3
|
+
"$id": "https://coda.pub/schemas/plugin/v2/capabilities.json",
|
|
4
4
|
"title": "contributes.capabilities:插件带来的能力",
|
|
5
5
|
"description": "能力名的数组,每个是 .coda/capabilities/<名>.bpmn;只能被调用(workflow_run、callActivity),没有触发器。",
|
|
6
6
|
"type": "array",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://coda.pub/schemas/plugin/
|
|
3
|
+
"$id": "https://coda.pub/schemas/plugin/v2/commands.json",
|
|
4
4
|
"title": "contributes.commands:放在会话与步骤 PATH 最前的命令",
|
|
5
5
|
"description": "命令名 → 由 sh 运行的一行命令(带上参数)。",
|
|
6
6
|
"type": "array",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://coda.pub/schemas/plugin/
|
|
3
|
+
"$id": "https://coda.pub/schemas/plugin/v2/common.json",
|
|
4
4
|
"title": "插件清单里共用的写法",
|
|
5
5
|
"$defs": {
|
|
6
6
|
"id": {"description": "小写字母、数字和「-」,以字母开头,至多 40 个字符", "type": "string", "pattern": "^[a-z][a-z0-9-]{0,39}$"},
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://coda.pub/schemas/plugin/
|
|
3
|
+
"$id": "https://coda.pub/schemas/plugin/v2/composer-modes.json",
|
|
4
4
|
"title": "contributes.composerModes:输入框模式",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "输入框能处在的模式:显示什么(图标、标题、发出去是什么、占位与发送按钮),发出的一条被收下后是否退出(once),工具栏始终使用输入框自己的执行者,在哪些输入框里能进(in:session 会话的、new 新任务的),参数(写法同设置)。打字进入的(trigger,如 !)没有参数,发出的就是会话的。",
|
|
6
6
|
"type": "array",
|
|
7
7
|
"maxItems": 10,
|
|
8
8
|
"items": {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"hint": {"$ref": "common.json#/$defs/line200"},
|
|
15
15
|
"placeholder": {"$ref": "common.json#/$defs/line40"},
|
|
16
16
|
"sendLabel": {"$ref": "common.json#/$defs/line20"},
|
|
17
|
-
"
|
|
17
|
+
"once": {"description": "true:这个模式里发出的一条被收下后,它从输入框退出", "type": "boolean"},
|
|
18
18
|
"trigger": {"description": "trigger 是一个标点:!#$%&*+:;=?^|~ 之一", "type": "string", "pattern": "^[!#$%&*+:;=?^|~]$"},
|
|
19
19
|
"in": {"description": "它出现的输入框:session、new,不重复", "type": "array", "minItems": 1, "uniqueItems": true, "items": {"enum": ["session", "new"]}},
|
|
20
20
|
"parameters": {"$ref": "#/$defs/parameters"}
|
|
@@ -25,15 +25,8 @@
|
|
|
25
25
|
{
|
|
26
26
|
"if": {"required": ["trigger"]},
|
|
27
27
|
"then": {
|
|
28
|
-
"description": "打字进入的模式(trigger
|
|
29
|
-
"not": {"
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
"if": {"properties": {"agent": {"const": "own"}}, "required": ["agent"]},
|
|
34
|
-
"then": {
|
|
35
|
-
"description": "开新会话的模式(agent: own)要从会话里发出,不能在 new 里",
|
|
36
|
-
"properties": {"in": {"description": "开新会话的模式(agent: own)要从会话里发出,不能在 new 里", "not": {"contains": {"const": "new"}}}}
|
|
28
|
+
"description": "打字进入的模式(trigger)没有参数",
|
|
29
|
+
"not": {"required": ["parameters"]}
|
|
37
30
|
}
|
|
38
31
|
}
|
|
39
32
|
]
|
|
@@ -51,8 +44,32 @@
|
|
|
51
44
|
"maxProperties": 20,
|
|
52
45
|
"propertyNames": {"description": "参数名只用字母和数字,以小写字母开头", "pattern": "^[a-z][A-Za-z0-9]{0,39}$"},
|
|
53
46
|
"additionalProperties": {
|
|
47
|
+
"properties": {
|
|
48
|
+
"presets": {
|
|
49
|
+
"description": "按表格设置的分组列填入预设,只用于项是对象的数组参数",
|
|
50
|
+
"type": "object",
|
|
51
|
+
"properties": {
|
|
52
|
+
"from": {"type": "string"},
|
|
53
|
+
"by": {"type": "string"}
|
|
54
|
+
},
|
|
55
|
+
"required": ["from", "by"],
|
|
56
|
+
"additionalProperties": false
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"unevaluatedProperties": false,
|
|
54
60
|
"allOf": [
|
|
55
|
-
{
|
|
61
|
+
{
|
|
62
|
+
"if": {"required": ["presets"]},
|
|
63
|
+
"then": {
|
|
64
|
+
"description": "presets 只用于 type: array 且 items.type: object 的参数",
|
|
65
|
+
"properties": {
|
|
66
|
+
"type": {"const": "array"},
|
|
67
|
+
"items": {"type": "object", "properties": {"type": {"const": "object"}}, "required": ["type"]}
|
|
68
|
+
},
|
|
69
|
+
"required": ["type", "items"]
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
{"$ref": "setting.json#/$defs/setting"},
|
|
56
73
|
{
|
|
57
74
|
"description": "参数不能是密文",
|
|
58
75
|
"not": {"properties": {"writeOnly": {"const": true}}, "required": ["writeOnly"]},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://coda.pub/schemas/plugin/
|
|
3
|
+
"$id": "https://coda.pub/schemas/plugin/v2/configuration.json",
|
|
4
4
|
"title": "contributes.configuration:插件的设置",
|
|
5
5
|
"description": "照 VS Code 的 contributes.configuration:properties 是设置名 → 声明(setting.json),设置名以「<插件 ID>.」开头;required 是缺了就不能用的设置。",
|
|
6
6
|
"type": "object",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://coda.pub/schemas/plugin/
|
|
3
|
+
"$id": "https://coda.pub/schemas/plugin/v2/endpoints.json",
|
|
4
4
|
"title": "contributes.endpoints:客户端发给插件的请求",
|
|
5
5
|
"description": "id → 回答它的一行命令(请求从标准输入给);checks 是它检查的本插件设置,客户端在那项设置旁提供「检查」,一项设置至多一个端点。",
|
|
6
6
|
"type": "array",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://coda.pub/schemas/plugin/
|
|
3
|
+
"$id": "https://coda.pub/schemas/plugin/v2/environment.json",
|
|
4
4
|
"title": "contributes.environment:给项目里启动的东西的环境变量",
|
|
5
5
|
"description": "变量名 → 值,不超过 100 个。值里可以写 ${config:<本插件的单值设置>}(不能是密文:密文从 CODA_PLUGIN_SECRETS 给的文件读)、${CODA_PLUGIN_PATH}、${CODA_PLUGIN_STATE}。PATH 由 contributes.path 给;Coda 自己的变量与 shell 的钩子不归插件。",
|
|
6
6
|
"type": "object",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://coda.pub/schemas/plugin/
|
|
3
|
+
"$id": "https://coda.pub/schemas/plugin/v2/events.json",
|
|
4
4
|
"title": "contributes.events:插件发布的事件",
|
|
5
5
|
"description": "流程订阅的事件名;starts(默认开)是否开始订阅它的流程,否则只送到在跑的运行;ends 是否结束它的主体(如 PR 关闭)。",
|
|
6
6
|
"type": "array",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://coda.pub/schemas/plugin/
|
|
3
|
+
"$id": "https://coda.pub/schemas/plugin/v2/executors.json",
|
|
4
4
|
"title": "contributes.executors:给这台机器脚本步骤的执行环境",
|
|
5
5
|
"description": "照 GitHub 的 container hooks:叫什么、有哪些标签(coda:runsOn 那样写),以及 hook——步骤的执行器为作业的每一部分调用的一行命令。",
|
|
6
6
|
"type": "array",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://coda.pub/schemas/plugin/
|
|
3
|
+
"$id": "https://coda.pub/schemas/plugin/v2/extension-points.json",
|
|
4
4
|
"title": "contributes.extensionPoints:插件声明的扩展点",
|
|
5
5
|
"description": "扩展点名 → 声明。只写 description 的,别的插件用 contributes.extensions 接一条命令;写了 contributes 的,任何插件在它的 contributes 里用这个键写的东西归它,原样交给声明它的插件读,合乎它的 schema 才装得上。",
|
|
6
6
|
"type": "object",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://coda.pub/schemas/plugin/
|
|
3
|
+
"$id": "https://coda.pub/schemas/plugin/v2/extensions.json",
|
|
4
4
|
"title": "contributes.extensions:接在别的插件扩展点上的命令",
|
|
5
5
|
"description": "「插件 ID/扩展点名」→ { command };那个插件须写在 extensionDependencies 里。",
|
|
6
6
|
"type": "object",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://coda.pub/schemas/plugin/
|
|
3
|
+
"$id": "https://coda.pub/schemas/plugin/v2/git-credentials.json",
|
|
4
4
|
"title": "contributes.gitCredentials:为一个 https 主机回答 Git 凭据",
|
|
5
5
|
"description": "url 是 https:// 加小写主机名(可带端口,不带路径);command 作为 Git 的凭据助手运行。只有 Coda 自带的插件装得上(安装时的规则)。",
|
|
6
6
|
"type": "array",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://coda.pub/schemas/plugin/
|
|
3
|
+
"$id": "https://coda.pub/schemas/plugin/v2/groups.json",
|
|
4
4
|
"title": "contributes.groups:并发组",
|
|
5
5
|
"description": "有名字、有容量的锁:name 是任何进程都能拿的组名,capacity 是本插件的一项整数设置,说同时放进几个。",
|
|
6
6
|
"type": "array",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://coda.pub/schemas/plugin/
|
|
3
|
+
"$id": "https://coda.pub/schemas/plugin/v2/instructions.json",
|
|
4
4
|
"title": "contributes.instructions:写给 Agent 的几句话",
|
|
5
5
|
"description": "装了这个插件的项目里,每个会话的指令都带上它:不超过 200 字。",
|
|
6
6
|
"type": "string",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://coda.pub/schemas/plugin/
|
|
3
|
+
"$id": "https://coda.pub/schemas/plugin/v2/mcp-servers.json",
|
|
4
4
|
"title": "contributes.mcpServers:给 Agent 的 MCP 服务器",
|
|
5
5
|
"description": "服务器名 → { command, args, env },照 Claude Code 的 .mcp.json:Agent 在本机启动、经标准输入输出说话的程序(stdio),不经 shell。command 与 args 里可以写 ${CODA_PLUGIN_PATH}、${CODA_PLUGIN_STATE}、${CODA_NODE};设置经 env 交给它(写法同 contributes.environment)。",
|
|
6
6
|
"type": "object",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://coda.pub/schemas/plugin/
|
|
3
|
+
"$id": "https://coda.pub/schemas/plugin/v2/model-providers.json",
|
|
4
4
|
"title": "contributes.modelProviders:插件提供的模型服务",
|
|
5
5
|
"description": "别的插件用 coda-model-provider 的设置按「插件/id」选它:叫什么、讲哪种接口、在哪(地址里可以写本插件的单值设置 ${config:…})、Key 是本插件的哪个密文。",
|
|
6
6
|
"type": "array",
|
package/schemas/plugin/path.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://coda.pub/schemas/plugin/
|
|
3
|
+
"$id": "https://coda.pub/schemas/plugin/v2/path.json",
|
|
4
4
|
"title": "contributes.path:放上 PATH 的目录",
|
|
5
5
|
"description": "插件目录里的目录(相对路径),或它在后端的状态目录 ${CODA_PLUGIN_STATE} 下的目录(setup 可以建它)。",
|
|
6
6
|
"type": "array",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://coda.pub/schemas/plugin/
|
|
3
|
+
"$id": "https://coda.pub/schemas/plugin/v2/plugin.json",
|
|
4
4
|
"title": "插件清单(插件目录的 package.json)",
|
|
5
5
|
"description": "照 VS Code 扩展的 package.json 写;Coda 读的是下面这些键,其余是 npm 的,不管。contributes 的每个键是一种类型,各有自己的 schema;不是 Coda 的键,是写给别的插件的扩展点的(它们声明的类型),由那个扩展点的 schema 校验。",
|
|
6
6
|
"type": "object",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://coda.pub/schemas/plugin/
|
|
3
|
+
"$id": "https://coda.pub/schemas/plugin/v2/repositories.json",
|
|
4
4
|
"title": "contributes.repositories:列出这台能访问的仓库",
|
|
5
5
|
"description": "一行命令,打印仓库供客户端选。只有 Coda 自带的插件装得上(安装时的规则)。",
|
|
6
6
|
"$ref": "common.json#/$defs/commandHolder"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://coda.pub/schemas/plugin/
|
|
3
|
+
"$id": "https://coda.pub/schemas/plugin/v2/services.json",
|
|
4
4
|
"title": "contributes.services:开启随包服务",
|
|
5
5
|
"description": "随后端包的独立服务的 ID;没有已安装插件开启的服务不运行。",
|
|
6
6
|
"type": "array",
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://coda.pub/schemas/plugin/
|
|
3
|
+
"$id": "https://coda.pub/schemas/plugin/v2/setting.json",
|
|
4
4
|
"title": "一项设置的声明",
|
|
5
5
|
"description": "插件用 JSON Schema 声明设置,Coda 读的是其中一部分:单个值;多选(items.anyOf 的常量);多个模型服务;一列行;表格(additionalProperties 是每一项)。表格的每一项是单个值、Agent 组合或几列;一列是单个值或 Agent 组合。格式只认 coda-agent(新会话用什么启动)、coda-agent-match(表格的行名:哪些会话)、duration(ISO 8601 时长)、coda-repo、coda-directory、coda-model-provider(别的插件提供的模型服务)、coda-model-url(模型服务的地址,Key 与接口写在同组的 modelKey、modelApi)。writeOnly 是密文:只写不读,放在数据库旁的文件里。",
|
|
6
6
|
"$ref": "#/$defs/setting",
|
|
7
|
+
"unevaluatedProperties": false,
|
|
7
8
|
"$defs": {
|
|
8
9
|
"base": {
|
|
9
10
|
"type": "object",
|
|
@@ -72,7 +73,6 @@
|
|
|
72
73
|
"additionalProperties": true,
|
|
73
74
|
"propertyNames": true
|
|
74
75
|
},
|
|
75
|
-
"unevaluatedProperties": false,
|
|
76
76
|
"allOf": [
|
|
77
77
|
{
|
|
78
78
|
"if": {"properties": {"writeOnly": {"const": true}}, "required": ["writeOnly"]},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://coda.pub/schemas/plugin/
|
|
3
|
+
"$id": "https://coda.pub/schemas/plugin/v2/setup.json",
|
|
4
4
|
"title": "contributes.setup:准备运行环境",
|
|
5
5
|
"description": "用户安装或更新插件后运行的一行命令。",
|
|
6
6
|
"$ref": "common.json#/$defs/commandHolder"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://coda.pub/schemas/plugin/
|
|
3
|
+
"$id": "https://coda.pub/schemas/plugin/v2/skills.json",
|
|
4
4
|
"title": "contributes.skills:插件带来的 Agent Skill",
|
|
5
5
|
"description": "插件目录里的目录,每个有自己的 SKILL.md;Agent 按目录名找到它,所以目录名不重复。",
|
|
6
6
|
"type": "array",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://coda.pub/schemas/plugin/
|
|
3
|
+
"$id": "https://coda.pub/schemas/plugin/v2/views.json",
|
|
4
4
|
"title": "contributes.views:客户端为插件打开的页面",
|
|
5
5
|
"description": "插件目录里的 HTML,客户端在沙箱里开:在哪常驻(side 会话旁、page 一页),图标,接管打开的引用种类(handles,像 VS Code 的自定义编辑器),读数(readout)与只给哪种工作位置的会话(checkout)。",
|
|
6
6
|
"type": "array",
|
package/validate.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// A plugin is right when what it writes is of the types it names: each key of a manifest's `contributes` is a type with a
|
|
2
|
-
// JSON Schema of its own (schemas/plugin/, `https://coda.pub/schemas/plugin/
|
|
2
|
+
// JSON Schema of its own (schemas/plugin/, `https://coda.pub/schemas/plugin/v2/…`), and a key that is no type of Coda's is
|
|
3
3
|
// one a plugin's extension point declares, for that plugin to read: of the schema that point names, a file of the declaring
|
|
4
4
|
// plugin's (`extensionPoints.<point>.schema`), when it names one. Checking one is checking it against those schemas,
|
|
5
5
|
// nothing else: a field joins a type by joining its schema, never by a rule here. Coda checks a plugin with this module
|
|
@@ -28,7 +28,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
28
28
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
29
29
|
|
|
30
30
|
export const SCHEMA_DIR = fileURLToPath(new URL('./schemas/plugin/', import.meta.url));
|
|
31
|
-
const BASE = 'https://coda.pub/schemas/plugin/
|
|
31
|
+
const BASE = 'https://coda.pub/schemas/plugin/v2/';
|
|
32
32
|
// The schemas, by file name.
|
|
33
33
|
export const SCHEMAS = Object.fromEntries(readdirSync(SCHEMA_DIR).filter(file => file.endsWith('.json')).sort()
|
|
34
34
|
.map(file => [file, JSON.parse(readFileSync(join(SCHEMA_DIR, file), 'utf8'))]));
|