Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/ADAPTER_SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,9 @@ adapter 应优先用 `events.poll` 观察 `action.updated`,再用 `actions.res

这些样例使用通用应用名,不绑定任何第三方项目。测试会校验样例的 manifest、checksum、权限和 payload 文件。

本机应用接入 local bridge 的最小请求流程见 [generic-adapter](examples/generic-adapter/)。
本机应用接入 local bridge 的最小请求流程见 [generic-adapter](examples/generic-adapter/)。那里有一个可执行的 `generic-adapter.mjs` 样板,直接串起 `export -> bundle.detail -> events.poll/actions.results -> bundle.import -> receipt -> rollback`。

样板里 `bundle.detail` 的只读预览状态用 `saved` / `imported`,`actions.results` 和 `events.poll` 里的动作状态用同一组生命周期词:`queued`、`running`、`succeeded`、`failed`、`conflict`、`cancelled`。

## 仍未实现

Expand Down
3 changes: 2 additions & 1 deletion docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ NekoDrop 已经有一个可用的 macOS / Windows 桌面互传主线:
目标:给上层数据传输建立统一包格式,不把 skills、session、agent profile 当作普通散文件乱传。

规格文档:[BUNDLE_SPEC.md](BUNDLE_SPEC.md)。当前已有协议模型、校验、staging、手动创建、收到后查看、删除、过期清理和导入到 NekoDrop 本机导入区;自动导出、导入计划预览和上层应用真实导入还没有完成。
`docs/examples/generic-adapter/` 现在提供了一个可执行的本机样板,串起 export、bundle.detail、events.poll、actions.results、bundle.import、receipt 和 rollback。它是适配边界的参照,不是对真实第三方应用的自动接入。

候选包类型:

Expand Down Expand Up @@ -152,7 +153,7 @@ local application

- 更完整的事件订阅,不只依赖短等待轮询
- 本机接入 UI 对待授权、待执行和失败原因的展示
- 通用 adapter 样例,让上层应用知道怎么导出和导入 bundle
- 通用 adapter 样例已经落成,后续继续补真实导入计划和冲突策略
- 导入计划和冲突策略

不做:
Expand Down
6 changes: 3 additions & 3 deletions docs/STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@
| iroh transport | 实验中 | 只有类型预留和明确错误,未接入 iroh runtime。 |
| Relay / P2P transport | 实验中 | 只有类型预留和明确错误。 |
| NekoLink bundle manifest | 部分接入 | [BUNDLE_SPEC.md](BUNDLE_SPEC.md) 已定义包结构、权限、校验和导入边界;`nekolink-protocol` 已有 bundle manifest、checksums、permissions 类型和校验,`nekodrop-storage` 已能识别、校验、保存到 staging,也能把用户选择的目录打成 v1 bundle;`nekodrop-service` 已有接收完成后的 staged bundle report。桌面端的资料包创建入口已收进发送页,收到的 staged bundle 在收件流程里查看、删除和手动导入到本机导入区;导入使用临时目录落盘,失败不留下半成品目标目录;桌面端会清理过期暂存,删除和导入失败状态会留在收件流程里。`bundle.send` 的本机 local bridge 执行入口现在可以消费待执行动作并交给桌面发送主线;`bundle.import` 可以消费待执行动作并把 staged bundle 导入本机导入区;同名导入会拒绝覆盖并返回 `bundle_import_conflict`。上层应用自动导出 session / skill / workspace 还没有接入。 |
| Adapter 规范和 bundle 样例 | 部分接入 | [ADAPTER_SPEC.md](ADAPTER_SPEC.md) 已定义上层应用导出/导入 bundle 的边界;[bundle-samples](bundle-samples/) 提供 `skill`、`session`、`workspace`、`agent_profile`、`config_snapshot` 五类可校验样例。真实上层应用 adapter 还没有接入,但本机 local bridge 的动作结果和短等待轮询已经有了。 |
| 本机 local bridge 协议模型 | 部分接入 | `nekolink-protocol` 已定义 `LocalBridgeRequest` / `LocalBridgeEvent` 的 JSON 模型,覆盖查询设备、申请本机授权、查询 staged bundle 详情、发送 bundle、收到 bundle 通知、请求导入、查询传输状态、查询动作结果和 `events.poll` 事件轮询;请求可以带本机 `client` 标识,授权申请已有通用 scope:`device.read`、`transfer.status.read`、`bundle.read`、`bundle.send`、`bundle.import.request`。桌面端内部 handler 可以把只读请求映射到可信设备、staged bundle 列表/详情和 transfer status,并区分 `read_only` / `requires_user_confirmation`、`anonymous` / `identified`;设置页可以触发一次内部 `devices.list` 只读自测,并显示 localhost runtime 的真实监听状态、地址、待确认授权、已授权数量、待执行动作数量和最近结果。桌面端启动时会开启只绑定 `127.0.0.1` 的 localhost runtime,只接受 `POST /bridge/request`,请求体有大小上限;只读请求和授权申请走同一套 handler。用户确认授权码后,runtime 会记录该 client 的限时权限并写入本机授权文件;下次启动会恢复未过期授权。已授权 client 调用 `bundle.send` / `bundle.import` 时,runtime 会把请求写入内存待执行队列,后台 worker 会自动消费;设置页可以查看概要并移除这些待执行动作;`bundle.send` 会先做 preflight,再复用现有 authenticated send 主线发送到目标设备;`bundle.import` 可以按 FIFO 消费待执行动作,并把 staged bundle 导入本机导入区,同名导入返回 `bundle_import_conflict`。动作生命周期会写入 `queued`、`running`、`succeeded`、`failed`、`conflict`、`cancelled`,授权 client 可通过 `events.poll` 的 `action.updated` 持续观察,也可用 `actions.results` 补偿查询自己的最新动作结果。普通列表、事件和结果都不暴露本机 `bundle_root`。runtime 现在有内存事件队列,真实发送/接收主流程会写入 `transfer.updated`,收到 staged bundle 会写入 `bundle.received`;已授权 client 可用 `events.poll` 轮询快照或短等待新事件。 |
| Adapter 规范和 bundle 样例 | 部分接入 | [ADAPTER_SPEC.md](ADAPTER_SPEC.md) 已定义上层应用导出/导入 bundle 的边界;[bundle-samples](bundle-samples/) 提供 `skill`、`session`、`workspace`、`agent_profile`、`config_snapshot` 五类可校验样例;[generic-adapter](examples/generic-adapter/) 现在提供一个可执行的本机样板,串起导出、只读预览、事件轮询、动作结果、导入、receipt 和 rollback。真实上层应用 adapter 还没有接入,但本机 local bridge 的动作结果和短等待轮询已经有了。 |
| 本机 local bridge 协议模型 | 部分接入 | `nekolink-protocol` 已定义 `LocalBridgeRequest` / `LocalBridgeEvent` 的 JSON 模型,覆盖查询设备、申请本机授权、查询 staged bundle 详情、发送 bundle、收到 bundle 通知、请求导入、查询传输状态、查询动作结果和 `events.poll` 事件轮询;请求可以带本机 `client` 标识,授权申请已有通用 scope:`device.read`、`transfer.status.read`、`bundle.read`、`bundle.send`、`bundle.import.request`。桌面端内部 handler 可以把只读请求映射到可信设备、staged bundle 列表/详情和 transfer status,并区分 `read_only` / `requires_user_confirmation`、`anonymous` / `identified`;设置页可以触发一次内部 `devices.list` 只读自测,并显示 localhost runtime 的真实监听状态、地址、待确认授权、已授权数量、待执行动作数量和最近结果。桌面端启动时会开启只绑定 `127.0.0.1` 的 localhost runtime,只接受 `POST /bridge/request`,请求体有大小上限;只读请求和授权申请走同一套 handler。用户确认授权码后,runtime 会记录该 client 的限时权限并写入本机授权文件;下次启动会恢复未过期授权。已授权 client 调用 `bundle.send` / `bundle.import` 时,runtime 会把请求写入内存待执行队列,后台 worker 会自动消费;设置页可以查看概要并移除这些待执行动作;`bundle.send` 会先做 preflight,再复用现有 authenticated send 主线发送到目标设备;`bundle.import` 可以按 FIFO 消费待执行动作,并把 staged bundle 导入本机导入区,同名导入返回 `bundle_import_conflict`。动作生命周期会写入 `queued`、`running`、`succeeded`、`failed`、`conflict`、`cancelled`,授权 client 可通过 `events.poll` 的 `action.updated` 持续观察,也可用 `actions.results` 补偿查询自己的最新动作结果。`bundle.detail` 只返回只读预览,不改变本机 staging;普通列表、事件和结果都不暴露本机 `bundle_root`。runtime 现在有内存事件队列,真实发送/接收主流程会写入 `transfer.updated`,收到 staged bundle 时会写入 `bundle.received`;已授权 client 可用 `events.poll` 轮询快照或短等待新事件。 |

## 当前不能宣传为已完成

Expand Down Expand Up @@ -114,7 +114,7 @@ V0.7

V0.8
NekoLink 上层包格式:
定义 bundle manifest,为 skills、session、agent profile、workspace 这类上层数据传输提供统一校验、权限和兼容边界;桌面端已有 staging、预览、删除、过期清理和手动导入到本机导入区;local bridge 已有 localhost runtime、授权、待执行队列、动作结果和事件轮询。下一步补导入计划、冲突策略和真实上层应用适配
定义 bundle manifest,为 skills、session、agent profile、workspace 这类上层数据传输提供统一校验、权限和兼容边界;桌面端已有 staging、预览、删除、过期清理和手动导入到本机导入区;local bridge 已有 localhost runtime、授权、待执行队列、动作结果和事件轮询。generic adapter 样板已经落到可执行脚本,但真实上层应用接入、导入计划和冲突策略还要继续补

V0.9
transport 技术验证:
Expand Down
167 changes: 24 additions & 143 deletions docs/examples/generic-adapter/README.md
Original file line number Diff line number Diff line change
@@ -1,157 +1,38 @@
# 通用 Adapter 示例
# 通用 Adapter 样板

这个示例说明一个本机应用如何接入 NekoDrop / NekoLink bundle。它不绑定任何具体应用
这个目录提供一个可执行的最小样板,说明本机应用怎样围绕 NekoLink bundle 走完整流程

## 导出

adapter 先把自己的数据导出成一个 bundle 目录:
## 文件

```text
exported-bundle/
bundle.json
checksums.json
permissions.json
files/
session.json
```

导出前必须做两件事:

- 移除 token、cookie、密钥、机器本地路径和账号私密标识。
- 如果不能确认已经脱敏,把 `permissions.json` 里的 `contains_secrets` 设为 `true`,这样接收端只能保存,不能导入。

## 授权

本机应用第一次发送或请求导入前,先申请权限:

```json
{
"kind": "authorization.request",
"payload": {
"request_id": "adapter-auth-001",
"client": {
"client_id": "generic.adapter",
"display_name": "Generic Adapter",
"app_kind": "agent"
},
"requested_scopes": [
"device.read",
"bundle.send",
"bundle.import.request",
"transfer.status.read"
],
"reason": "Send and import user-selected bundles",
"ttl_seconds": 3600
}
}
```

NekoDrop 会返回短授权码。用户在设置 -> 接入里确认后,后续请求才会进入待执行队列。

## 发送

```json
{
"kind": "bundle.send",
"payload": {
"request_id": "adapter-send-001",
"client": {
"client_id": "generic.adapter",
"display_name": "Generic Adapter",
"app_kind": "agent"
},
"target_device_id": "neko-device-target",
"bundle_root": "/absolute/path/to/exported-bundle",
"bundle_type": "session",
"require_trusted_device": true
}
}
generic-adapter.mjs
generic-adapter.test.mjs
```

请求成功只代表动作入队,不代表已经发送完成。桌面端后台 worker 会自动做 preflight 和真实发送。adapter 优先用 `events.poll` 里的 `action.updated` 观察进度,再用 `actions.results` 查最新结果。
## 流程

## 查询结果
1. 导出一个已经校验过的 bundle 目录。
2. 生成 `authorization.request`、`bundle.send`、`bundle.detail`、`events.poll`、`actions.results`、`bundle.import` 请求。
3. 先看 `bundle.detail` 的只读预览,再看 `actions.results` 和 `events.poll` 的状态词。
4. 导入后写 receipt。
5. 需要撤销时删除导出目录。

```json
{
"kind": "actions.results",
"payload": {
"request_id": "adapter-results-001",
"client": {
"client_id": "generic.adapter",
"display_name": "Generic Adapter",
"app_kind": "agent"
},
"after_claimed_at_ms": null,
"limit": 20
}
}
```

结果里的 `lifecycle_status` 可能是:

- `queued`
- `running`
- `succeeded`
- `failed`
- `conflict`
- `cancelled`

旧字段 `status` 仍会保留给兼容代码。新 adapter 应优先读 `lifecycle_status`。

常见 `reason`:

- `bundle_root_missing`
- `bundle_invalid`
- `bundle_type_mismatch`
- `trusted_target_missing`
- `bundle_send_failed`
- `bundle_import_conflict`
- `bundle_import_failed`
## 运行

## 等待事件

`events.poll` 默认立即返回快照。需要减少轮询时,可以加 `timeout_ms`:

```json
{
"kind": "events.poll",
"payload": {
"request_id": "adapter-events-001",
"client": {
"client_id": "generic.adapter",
"display_name": "Generic Adapter",
"app_kind": "agent"
},
"after_event_id": null,
"limit": 20,
"timeout_ms": 15000
}
}
```bash
node docs/examples/generic-adapter/generic-adapter.mjs export --out /tmp/generic-adapter-bundle
node docs/examples/generic-adapter/generic-adapter.mjs plan --bundle /tmp/generic-adapter-bundle
node docs/examples/generic-adapter/generic-adapter.mjs receipt --bundle /tmp/generic-adapter-bundle --receipt-out /tmp/generic-receipt.json
node docs/examples/generic-adapter/generic-adapter.mjs rollback --bundle /tmp/generic-adapter-bundle
```

`action.updated` 事件会带 `request_id`、`action_kind`、`status`、`reason`、`bundle_id`、`bundle_type` 和 `target_device_id`。事件不会返回本机 `bundle_root`。

这只是本机短等待,不是公网长连接。
## 样板约定

## 导入
- `bundle.detail` 只做只读预览,预览状态是 `saved`;导入后的 receipt 用 `imported`。
- `actions.results` 和 `events.poll` 共享同一组动作状态词:`queued`、`running`、`succeeded`、`failed`、`conflict`、`cancelled`。
- `bundle.import` 只接受已经暂存的 bundle,不直接写第三方应用目录。
- `rollback` 只清理导出目录,不撤销 NekoDrop 侧已经完成的导入动作。

接收端 adapter 不直接从任意路径导入。它先请求 NekoDrop 导入 staged bundle 到本机导入区:

```json
{
"kind": "bundle.import",
"payload": {
"request_id": "adapter-import-001",
"client": {
"client_id": "generic.adapter",
"display_name": "Generic Adapter",
"app_kind": "agent"
},
"staged_bundle_id": "bundle_1234567890",
"expected_bundle_type": "session"
}
}
```
## 说明

如果同名 bundle 已经存在,NekoDrop 不覆盖,会返回 `bundle_import_conflict`。adapter 需要让用户选择重命名、跳过或合并
这个样板只用通用应用名,不绑定任何第三方项目。它的目标是让真实 adapter 的最小实现有一个稳定参照,不是新增一套协议
Loading
Loading