dsh-hitl 0.2.0 → 0.2.1

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 CHANGED
@@ -99,17 +99,19 @@ export function apply(ctx) {
99
99
  }
100
100
  ```
101
101
 
102
- > **Always pass `owner` (the third argument).** Mounts are state owned by the `hitl` plugin, not by yours. Without an owner, the disposer `protect()` returns is the only way to unbind — and "return a function from `apply`" is **not** a lifetime: it was measured that the mount outlives the unloaded plugin row, leaving that tool blocked forever. With `ctx`, `hitl` registers an effect on your plugin's context and the mount is released when your plugin unloads. The equivalent explicit form is to wrap it in your own effect: `ctx.effect(() => ctx.hitl.protect('bash', {...}), 'my-plugin: hitl mount')`. If a mount does linger, look at `ctx.hitl.list()` and clear it with `ctx.hitl.unprotect('bash')`, or toggle the `hitl` row off and on in the plugins page (which rebuilds the plugin's state).
102
+ > **`owner` decides whose account this mount's revocation is filed under.** It is the calling plugin's Cordis context — normally the `ctx` your `apply` received: pass it and `hitl` files the mount's release on the calling plugin's fiber, so the mount is removed automatically when that plugin is unloaded, disabled, or reloaded. The mount itself is state owned by the `hitl` plugin, not by yours — so without an owner the disposer `protect()` returns is the only way to unbind, and "return a function from `apply`" is **not** a lifetime: it was measured that the mount outlives the unloaded plugin row, leaving that tool blocked forever. The equivalent explicit form is to wrap it in your own effect: `ctx.effect(() => ctx.hitl.protect('bash', {...}), 'my-plugin: hitl mount')`. If a mount does linger, look at `ctx.hitl.list()` and clear it with `ctx.hitl.unprotect('bash')`, or toggle the `hitl` row off and on in the plugins page (which rebuilds the plugin's state).
103
103
 
104
104
  ### 2.3 Service API
105
105
 
106
106
  | Member | Purpose |
107
107
  |---|---|
108
- | `protect(matcher, options?, owner?)` | Mount one tool; pass the caller's ctx as `owner` for automatic unbinding; returns a disposer |
108
+ | `protect(matcher, options?, owner?)` | Mount one tool; returns a disposer. Pass the caller's own ctx as `owner` to file this mount's unbinding under that plugin's lifetime: the mount is removed automatically when the caller is unloaded, disabled, or reloaded. Omit it and the mount outlives its caller |
109
109
  | `unprotect(matcher)` | Remove the mounts a matcher describes; returns how many were removed |
110
110
  | `list()` | Every current mount (diagnostics) |
111
111
  | `pending()` | Every request currently waiting for a human (diagnostics) |
112
112
 
113
+ > `owner` is optional in the signature because there is a second entry point: the rows in `config.protect` belong to the `hitl` plugin itself (they are cleared when that row unloads) and need no owner. On the `ctx.hitl.protect()` API, however, it is required in practice — omitting it is not a legal simplification but a trap.
114
+
113
115
  `matcher`: a tool name / a glob string containing `*` / a `RegExp` / an array of those / `(exec) => boolean`.
114
116
  When one tool is mounted several times, **the most recent mount wins** (the config row registers first and plugins later — so a plugin can override the config).
115
117
 
@@ -433,7 +435,7 @@ dsh-hitl/
433
435
  │ └── manifest.test.js 77 (4) package metadata and the locale resource shape
434
436
  ├── .github/workflows/release.yml tag-triggered publish via npm trusted publishing (OIDC)
435
437
  ├── package.json 68 manifest: exports / dsh.bundle.patch / dsh.client / icon
436
- ├── cordis.patch.yml 17 the bundle's configuration layer (inserts the row with id `hitl`)
438
+ ├── cordis.patch.yml 18 the bundle's configuration layer (inserts the row with id `hitl`)
437
439
  ├── icon.svg 6 the plugin list icon
438
440
  ├── LICENSE MIT
439
441
  ├── README.md this document
package/README.zh.md CHANGED
@@ -105,10 +105,12 @@ export function apply(ctx) {
105
105
  }
106
106
  ```
107
107
 
108
- > **一定要带 `owner`(第三个参数)**:挂载是记在 `hitl` 插件里的状态,不是你的插件状态。
109
- > 不传 owner 时,`protect()` 返回的 disposer 就是唯一的解绑手段——而"从 `apply` 里 return 一个函数"
108
+ > **`owner` 决定这次挂载的"撤销权"记在谁账上。** 它是调用方插件的 Cordis 上下文,通常就是你
109
+ > `apply` 收到的 `ctx`:传了它,`hitl` 会把这次挂载的解绑登记在调用方插件的 fiber 上,
110
+ > 该插件被卸载 / 禁用 / 重载时,挂载自动移除。
111
+ > 挂载本身是记在 `hitl` 插件里的状态,不是你的插件状态——所以不传 owner 时,`protect()` 返回的
112
+ > disposer 就是唯一的解绑手段,而"从 `apply` 里 return 一个函数"
110
113
  > **不算生命周期**:实测过,插件行被卸载后挂载仍然生效,那个工具会被永久拦下去。
111
- > 传了 `ctx` 之后,`hitl` 会在你的插件上下文上注册一个 effect,插件卸载即自动解绑。
112
114
  > 另一种等价写法是把它包进你自己的 effect:
113
115
  > `ctx.effect(() => ctx.hitl.protect('bash', {...}), 'my-plugin: hitl mount')`。
114
116
  > 万一留下了残留挂载,用 `ctx.hitl.list()` 看一眼,`ctx.hitl.unprotect('bash')` 清掉;
@@ -118,11 +120,15 @@ export function apply(ctx) {
118
120
 
119
121
  | 成员 | 说明 |
120
122
  |---|---|
121
- | `protect(matcher, options?, owner?)` | 挂载一个工具;`owner` 传调用方 ctx 即可自动解绑;返回 disposer |
123
+ | `protect(matcher, options?, owner?)` | 挂载一个工具,返回 disposer。`owner` 需传入调用方自己的 ctx,用于把这次挂载的解绑登记到调用方插件的生命周期上:调用方被卸载 / 禁用 / 重载时自动移除;不传则挂载会活过调用方 |
122
124
  | `unprotect(matcher)` | 按 matcher 描述移除挂载,返回移除数量 |
123
125
  | `list()` | 当前所有挂载(诊断用) |
124
126
  | `pending()` | 当前所有等待人类决策的请求(诊断用) |
125
127
 
128
+ > 签名上 `owner` 是可选的,因为还有另一条入口:`config.protect` 那些行天然归 `hitl` 自己管
129
+ > (它们随 `hitl` 这一行卸载而清空),不需要 owner。但用 `ctx.hitl.protect()` 这个 API 时,
130
+ > 它就是必需的——不传不是"一种合法的简化",而是一个坑。
131
+
126
132
  `matcher`:工具名 / 含 `*` 的通配串 / `RegExp` / 上面几者的数组 / `(exec) => boolean`。
127
133
  同一个工具被挂载多次时,**后挂载的覆盖先挂载的**(配置行先注册,插件后注册,所以插件可以覆盖配置)。
128
134
 
@@ -495,7 +501,7 @@ dsh-hitl/
495
501
  │ └── manifest.test.js 77 (4)包元数据与 locale 资源形状
496
502
  ├── .github/workflows/release.yml tag-triggered publish via npm trusted publishing (OIDC)
497
503
  ├── package.json 68 清单:exports / dsh.bundle.patch / dsh.client / icon
498
- ├── cordis.patch.yml 17 bundle 的配置层(插入 id 为 hitl 的那一行)
504
+ ├── cordis.patch.yml 18 bundle 的配置层(插入 id 为 hitl 的那一行)
499
505
  ├── icon.svg 6 插件列表图标
500
506
  ├── LICENSE MIT
501
507
  ├── README.md 本文档(英文版)
package/cordis.patch.yml CHANGED
@@ -2,10 +2,11 @@
2
2
  #
3
3
  # The row's `config.protect` list mounts HITL on tools without writing code:
4
4
  # each entry is { tool, ...hitlOptions } and mirrors ctx.hitl.protect(tool, options).
5
- # Plugins mount through the `hitl` service instead:
5
+ # Plugins mount through the `hitl` service instead — note the third argument:
6
+ # the callback's own ctx, so the mount is released when your plugin unloads.
6
7
  #
7
8
  # ctx.inject(['hitl'], (ctx) => {
8
- # ctx.hitl.protect('bash', { countdown: { seconds: 30, action: 'reject' } })
9
+ # ctx.hitl.protect('bash', { countdown: { seconds: 30, action: 'reject' } }, ctx)
9
10
  # })
10
11
  #
11
12
  # A patch replaces the whole `config` of a row it overrides, so keep every key
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-hitl",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Human-in-the-loop gate for DSH tool calls: mount any tool behind an approve / modify / reject decision card in the DSH Web UI, with an optional countdown.",
5
5
  "keywords": [
6
6
  "dsh",