dsh-plugin-logicmargin 0.0.0-stage → 0.9.18
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 +21 -0
- package/README.md +36 -2
- package/cordis.patch.yml +3 -0
- package/icon.svg +17 -0
- package/index.js +29 -0
- package/locale/en.json +1 -0
- package/locale/zh.json +1 -0
- package/native.js +91 -0
- package/package.json +19 -4
- package/resources/logic-margin/THIRD_PARTY_NOTICES.md +941 -0
- package/resources/logic-margin/plugin.json +4 -0
- package/resources/logic-margin/skills/logic-margin/SKILL.md +97 -0
- package/resources/logic-margin/skills/logic-margin/references/mcp-workflow.md +43 -0
- package/resources/logic-margin/skills/logic-margin/references/plugin-updates.md +9 -0
- package/resources/logic-margin/skills/logic-margin/references/review-format.md +156 -0
- package/resources/logic-margin/skills/logic-margin/scripts/logic-margin.ps1 +105 -0
- package/resources/logic-margin/skills/logic-margin/scripts/logic-margin.sh +91 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: logic-margin
|
|
3
|
+
description: 用 LogicMargin 将业务逻辑整理为可批注流程图,发起人工评审,并在收到意见后修改、回复和发布新版;用于逻辑图评审、恢复会话或安装更新 LogicMargin 插件。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# LogicMargin 逻辑评审
|
|
7
|
+
|
|
8
|
+
在当前开发工程工作。LogicMargin 的一个后台进程同时提供 HTTP MCP、评审 API 和网页;源码修改、测试和 Git 操作仍由原 Agent 在授权工作区完成。
|
|
9
|
+
|
|
10
|
+
用户要求安装、检查或更新插件时,读取 [插件安装与更新](references/plugin-updates.md),核对实际安装结果后报告。首次连接、工具缺失或连接故障时,读取 [连接、恢复与 CLI 回退](references/mcp-workflow.md)。
|
|
11
|
+
|
|
12
|
+
## 确定评审访问方式
|
|
13
|
+
|
|
14
|
+
新建评审前,先结合已有对话识别 Agent 的执行主机与用户打开评审页的设备,检查当前电脑实际可用的网络连接和可达地址。根据这些事实提出适合用户访问设备的选项,用实际设备名或地址说明访问范围,不预设固定选项或特定网络服务。用户设备信息尚不明确且影响判断时再询问。
|
|
15
|
+
|
|
16
|
+
访问范围尚未明确时,先询问用户并等待选择,再配置共享、创建和打开评审页面。已有明确选择时核验并沿用;同一会话恢复或发布新版不重复询问。确认访问方式后,按对应监听地址和可达地址生成分享链接,核对的范围不足以证明跨设备可达时如实说明。
|
|
17
|
+
|
|
18
|
+
## 建立连接
|
|
19
|
+
|
|
20
|
+
此技能由 DeepSeek Harness 插件注册;插件使用官方 MCP Client 接入本机 Go 服务,工具名为 `mcp__logic_margin__start_review`、`mcp__logic_margin__wait_review` 等。先从本技能 scripts/ 定位固定版本 launcher,记为 `LM_CLI`,通过宿主终端运行 `server status` 核对身份、版本和原端口,再实际调用只读 `mcp__logic_margin__list_reviews`(优先已知 context)。成功返回才确认当前 MCP 链路可用,工具名字或 HTTP 健康不足以证明握手成功。
|
|
21
|
+
|
|
22
|
+
握手或工具装载失败时,按 [DSH 连接与恢复](references/mcp-workflow.md) 区分启动器/制品、HTTP 服务和宿主连接,重载插件尝试一次;后台健康兼容而 MCP 仍不可用,就在原任务授权内用原生 CLI 回退,等待使用 `wait SESSION_ID --no-consume`,实际读完反馈后再领取。保留同一 session 和已处理游标,说明宿主长等待限制。
|
|
23
|
+
|
|
24
|
+
服务不存在时按参考安全执行 `server start`。不要运行 Codex 专用 `server setup`。安装/更新按 [DSH 插件安装与更新](references/plugin-updates.md) 执行,不使用网页的 Codex 一键更新。MCP 与源码工作区同机;网页共享仍先按前节确认访问范围,MCP 保持本机边界。
|
|
25
|
+
|
|
26
|
+
## 发起审查
|
|
27
|
+
|
|
28
|
+
下文 MCP 工具名描述正常通道。已进入 CLI 回退时,按连接参考用等价 CLI 贯穿核验、读取、等待、领取、发布和清理;MCP 恢复后先核对原 session 与已处理游标,再切回工具通道。
|
|
29
|
+
|
|
30
|
+
用户给出 session ID 或带 `?session=...` 的链接并要求继续时,转到“恢复会话”。每个独立目的分别调用 `start_review`,取得独立 session ID;同一目的的多轮修改沿原 session 发布新版。`context` 填已知的实际 thread/workflow 标识,没有时省略;相同 context 或评审文件 ID 不代表复用会话。
|
|
31
|
+
|
|
32
|
+
读取目标源码和工作区状态,明确本次覆盖范围与尚未验证部分。生成 JSON 前读取 [评审格式](references/review-format.md),按实际来源整理场景、规则、输入输出和源码依据,沿用仍代表同一业务对象的稳定节点/连线 ID。
|
|
33
|
+
|
|
34
|
+
已有代码改动待审时,先根据明确的 Git 基线在独立旧树中读取修改前源码,生成固定基线 B,再根据当前代码生成 R1。先讨论原系统改造时,从当前真实原版 B 发起评审,收到意见后再修改源码并发布 R1。两条流程都沿用同一评审 ID 和仍代表同一对象的节点/连线 ID,材料与核验要求见 [固定基线 B](references/review-format.md#固定基线-b)。
|
|
35
|
+
|
|
36
|
+
调用 `start_review`,参数使用本机绝对路径:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"review_file": "/path/to/review.logic-margin.json",
|
|
41
|
+
"source_root": "/path/to/source-workspace",
|
|
42
|
+
"baseline_file": "/path/to/before.logic-margin.json",
|
|
43
|
+
"baseline_source_root": "/path/to/baseline-workspace",
|
|
44
|
+
"purpose": "本次实际核对的行为"
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
它在创建前核验真实来源,失败时修正流程和依据后重试。`context`、分享链接 `public_url` 可按实际需要传入;用户要求通过后归档时设 `cleanup_on_approval: true`。`verify_review` 可用于只核验来源,不创建任务。
|
|
49
|
+
|
|
50
|
+
基线已嵌入 `review_file` 的 `baseline` 字段时,传 `baseline_source_root` 核验旧树即可。B 在后续 R2、R3 中保持固定;`base_round` 继续填写实际处理的评审轮次。
|
|
51
|
+
|
|
52
|
+
代码对照仅显示固定 B/R 保存的源码片段;后端可使用发布时按版本核验的完整源码缓存做结构分析,再投影回原片段。历史缺失时说明缺失,不补读当前工作区。结构重点和明确移动块的原位置局部对照是阅读辅助,移动时又修改的内容也需核对;不支持、配对不确定或预算耗尽时保留文本差异,不据此判断通用语义等价。
|
|
53
|
+
|
|
54
|
+
从原系统 B 开始收集改造意见时,`review_file` 提供原版图,`source_root` 指向其真实源码,设置 `baseline_review: true`,省略独立的基线文件与目录。工具核验原版一次并固定为 B,页面先显示 B,第一次实现发布后显示 R1。
|
|
55
|
+
|
|
56
|
+
创建成功后立即把完整审查链接给用户,再调用 `wait_review`,传入 `session_id`。工具内部持续等待,保存单条批注不会返回;只有人工明确提交、确认通过或出现终态错误才交回控制权。正常等待期间保持这一调用,不另行反复 `get_review` 检查。
|
|
57
|
+
|
|
58
|
+
## 处理反馈并发布新版
|
|
59
|
+
|
|
60
|
+
读取 `wait_review` 返回的固定提交。反馈及 `earlierSubmissions` 保留意见内容,需逐份核对;记录 session ID、实际审阅的 `round`、revision 和提交 ID。等待工具不领取、不归档,取消或断线后仍可重取原提交。
|
|
61
|
+
|
|
62
|
+
通过结果使用精简摘要与 `sessionId`、`submission.id` 索引,提供 `resolvedCommentCount`,不带批注、回复或源码文件清单。通过结果的 `submission.review.snapshot` 只保留 repository、commit、fingerprint、capturedAt 和 dirty;更早未领取反馈以 `earlierSubmissionIds` 列出。需要补查这些旧反馈或完整来源时,用 `get_review` 读取完整历史;该工具也能读取归档。
|
|
63
|
+
|
|
64
|
+
反馈结果 `approved: false`:先实际读取意见,再调用 `acknowledge_review`,传入 `session_id`,并将返回的 `submission.id` 作为 `submission_id`。领取只证明已取回意见,不代表修改完成。需要完整图或历史时用 `get_review`;临时回复用 `reply_review`,沿用原 comment ID。
|
|
65
|
+
|
|
66
|
+
逐条核对意见,在授权工作区修改并验证。更新评审文件的 `revision`、来源快照、指纹和依据;最终回应写入 `[{"commentId":"原批注 ID","body":"实际修改、验证与剩余限制"}]` 的 JSON 文件。
|
|
67
|
+
|
|
68
|
+
从 B 收到的改造意见带 `basis: "baseline"`。按意见实际修改源码、验证后,再生成实现图并用 `revise_review` 发布;`base_round` 始终取工具返回的实际 `round`,B/R1/R2 是页面版本名称。改造后的图与固定 B 一起保留,供人工核对实际实现。
|
|
69
|
+
|
|
70
|
+
调用 `revise_review`,使用本次实际处理的轮次:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"session_id": "SESSION_ID",
|
|
75
|
+
"review_file": "/path/to/updated.logic-margin.json",
|
|
76
|
+
"source_root": "/path/to/source-workspace",
|
|
77
|
+
"base_round": 1,
|
|
78
|
+
"summary": "本轮实际变化",
|
|
79
|
+
"replies_file": "/path/to/replies.json"
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
流程逻辑未变时填写 `unchanged_reason`,说明原因与覆盖范围。来源摘要只核验文件版本,业务语义仍需据源码整理。出现版本或轮次冲突时读取最新状态并重新核对,不能只改轮次数字重发旧草稿。
|
|
84
|
+
|
|
85
|
+
新版图、最终回应与发布记录原子保存后开启下一轮。将审查链接给用户,继续 `wait_review`;需要明确游标时,`after_submission` 使用最后实际处理的固定提交 ID。临时回复不会替人确认解决;代理不能自行解决批注或批准评审。
|
|
86
|
+
|
|
87
|
+
结果 `approved: true` 表示人已确认返回的快照通过;核对 round、revision 和 fingerprint 属于当前待评审快照后,结束评审循环,旧轮批准不能代替新版批准。若已授权通过后清理,并且结果返回 `cleanupPending: true`,在实际读到结果后调用 `cleanup_review` 归档;归档失败时保留通过结果和服务端数据,再核对最新状态。是否提交、推送或部署仍按原任务授权。
|
|
88
|
+
|
|
89
|
+
## 恢复会话与清理
|
|
90
|
+
|
|
91
|
+
按用户提供的原 session ID 调用 `get_review`,核对目的、上下文、来源工程、固定提交、轮次和已做回复。需要查找时用 `list_reviews` 的实际 `context` 与 `state: "all"`,确认目标后继续,不另建任务跳过旧意见。已归档任务可读取;需要继续修改时用 `restore_review` 恢复同一 ID。
|
|
92
|
+
|
|
93
|
+
旧会话需要前后对比而缺少 B 时,先按真实任务起点生成并核验原版图,再调用 `attach_review_baseline`,传原 `session_id`、`baseline_file` 和修改前树的 `source_root`。补录沿用当前轮次和批注,版本冲突时重新读取原会话后核对。
|
|
94
|
+
|
|
95
|
+
上下文中断后,从固定历史恢复尚未处理的意见。完成处理后再发布新版;继续等待时用已核实的 `after_submission` 游标。超时、中断或连接失败都不等于通过;服务故障按连接参考检查和恢复原后台。
|
|
96
|
+
|
|
97
|
+
`cleanup_review` 默认只归档已通过评审,完整图、批注和历史可恢复。清理未完成任务须有用户明确要求,并设置 `force: true`;永久删除还须有明确删除授权并设置 `mode: "delete"`。一次操作针对一个已确认的 session ID,context 查询不触发批量清理。
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# DeepSeek Harness 连接、恢复与 CLI 回退
|
|
2
|
+
|
|
3
|
+
此包由 DSH 加载器启动固定版本 Go CLI `server start`,连接官方 MCP Client 并注册随包技能。工具名为 `mcp__logic_margin__start_review`、`mcp__logic_margin__wait_review` 等。DSH 使用自己的插件重载,不运行 Codex 专用 `server setup`,不修改 Codex 配置。
|
|
4
|
+
|
|
5
|
+
`LM_CLI` 是本技能 `scripts/logic-margin.sh`(Windows 为 `logic-margin.ps1`)的实际绝对路径;同目录有原生程序时优先使用。首次运行会下载并校验固定版本官方包。CLI、MCP 与待核验源码须在同机;远程 provider 的文件不因本机连接就可读取。
|
|
6
|
+
|
|
7
|
+
## 独立检查服务与当前 MCP 链路
|
|
8
|
+
|
|
9
|
+
用宿主终端执行 `"$LM_CLI" server status`(PowerShell 为 `& $LM_CLI server status`),核对服务身份、版本、home、dataDir 和原端口。再实际调用只读 `mcp__logic_margin__list_reviews`,优先传已知 `context`,成功返回才确认当前 DSH MCP 连接可用。插件已加载、工具名已列出或状态有 `mcp: true`,都不能代替初始化与实际调用。
|
|
10
|
+
|
|
11
|
+
本机网络/进程探测若被沙箱权限拒绝,先按宿主许可路径复核,不把 `Operation not permitted` 当作服务退出,也不据此启动第二个写入者。
|
|
12
|
+
|
|
13
|
+
区分故障层次:
|
|
14
|
+
|
|
15
|
+
- launcher 下载失败、固定版 Release/附件 404:先核对包版本与公开制品。main 可能先于对应发行,不静默改清单或下载其他版本绕过校验;按 [DSH 安装更新](plugin-updates.md) 使用已公开的匹配包。这不是 HTTP 或 MCP 已不健康的证据。
|
|
16
|
+
- 服务未运行且原进程已退出:安全时用 `server start`,保留原登记、端口和数据。进程仍活但不健康、身份/版本冲突时保留现场,按已授权范围诊断恢复,不盲杀、删锁或启动第二个写入者。
|
|
17
|
+
- HTTP 健康兼容而握手/工具装载失败:重载此 DSH 插件尝试一次,再做只读调用;仍失败就在原任务授权内使用下节 CLI 回退,明确当前通道。来源、参数和轮次错误按业务错误处理。
|
|
18
|
+
|
|
19
|
+
一次 `wait_review` 预算为 86400000 毫秒,在工具内等待,不调用模型、不领取或归档;宿主、网络、进程仍可结束等待。正常等人工提交不是故障。重连后用 `get_review` 核对原 session 和固定历史,从最后实际处理的 submission ID 继续;连接重试不保证续接旧的在途调用。
|
|
20
|
+
|
|
21
|
+
## 安全 CLI 回退
|
|
22
|
+
|
|
23
|
+
沿用实际 `LOGIC_MARGIN_HOME`、`LOGIC_MARGIN_DATA_DIR`、端口、session ID 与已处理游标。等待前检查 `"$LM_CLI" --help` 是否支持 `--no-consume`;旧 CLI 仍可用 `list`、`get`、`verify` 只读诊断和恢复读取,不支持该标志时说明安全等待的版本限制,不能去掉标志使用会自动领取的等待。
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
"$LM_CLI" get SESSION_ID
|
|
27
|
+
"$LM_CLI" wait SESSION_ID --no-consume
|
|
28
|
+
"$LM_CLI" wait SESSION_ID --after SUBMISSION_ID --no-consume
|
|
29
|
+
"$LM_CLI" ack SESSION_ID SUBMISSION_ID
|
|
30
|
+
"$LM_CLI" cleanup SESSION_ID --mode archive
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
需要新建时,先用 `start` 取得 session ID/链接并交给用户,再等待;核验、基线、修订和回复的文件参数仍按共享评审格式传入。写请求发出而响应丢失时,先 `list --context ... --state all` / `get` 核对原轮次及发布记录,确认未生效后再重试,避免重复创建或发布。
|
|
34
|
+
|
|
35
|
+
`wait --no-consume` 与 MCP 一样只交付固定结果:反馈带 `requiresAcknowledgement`,通过且有清理策略时带 `cleanupPending`。实际读完反馈后显式 `ack`;核对通过快照且已获清理授权后显式 `cleanup`。取消或超时不会消费。默认 CLI 等待的旧自动领取/归档语义仍保留,本技能回退不使用它。
|
|
36
|
+
|
|
37
|
+
CLI 长等待能否持续挂起并自动唤醒由 DSH 终端能力决定。支持时保留同一进程;没有自动唤醒时报告限制、保留会话和句柄,不反复 `get` 或启动新等待模拟 MCP,也不把中断当通过。
|
|
38
|
+
|
|
39
|
+
## 网页访问与共享服务
|
|
40
|
+
|
|
41
|
+
新建或首次打开评审前,按共享技能先检查实际执行主机、访问设备和可用网络,未明确范围就询问并等待。恢复同一会话或发布新版沿用仍适用的已核实选择。分享链接和监听必须匹配;`0.0.0.0` 监听所有 IPv4 接口,`public_url` 不会改绑定或防火墙,本机打开不证明跨设备可达。跨设备只分享网页,MCP 仍限本机。
|
|
42
|
+
|
|
43
|
+
仅卸载 DSH 插件撤销其工具和技能,不停止可能供其他宿主共用的后台。版本调整按授权和备份流程保留同一数据目录、端口与 session,不修改真实 Codex 安装。
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# DeepSeek Harness 安装与更新
|
|
2
|
+
|
|
3
|
+
安装由 DSH 宿主插件管理器或 `dsh plugin --profile <实际名称> add <已验证制品>` 完成;当前兼容基线为 `0.2.2-rc.1`。不要把 Codex 插件缓存、marketplace 或 `server setup` 当作 DSH 的安装入口。
|
|
4
|
+
|
|
5
|
+
更新前按 `server status` 确认实际原生服务版本、实例与数据目录。保存服务数据备份;保留原 home、端口与 session ID。宿主替换本包并重载后,按新入口核对后台与 MCP 工具是否已实际切换;版本不兼容时不要自动覆盖或关闭共享的旧服务,按用户授权进行身份确认的停服和重启。
|
|
6
|
+
|
|
7
|
+
当前网页一键更新只处理 Codex 插件,不能用来更新 DSH 包。即使本机同时装有 Codex,DSH 的包版本仍需由 DSH 自己的安装器更新。安装、后台版本、技能发现、MCP 可调用是分别验收的结果。更新不代表评审通过,也不删除会话、B/R 快照、批注或归档。
|
|
8
|
+
|
|
9
|
+
npm 发布、社区目录收录、Discover 索引和 DeepSeek 账号登录互不等同。本包的本地 tgz 可用于隔离安装验证;只有公开发布和指定市场可发现性分别验收后,才可报告已经上架。
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# 评审 JSON 格式
|
|
2
|
+
|
|
3
|
+
生成或更新流程图时使用本页。以下字段对应 LogicMargin 的 `schemaVersion: 1`;CLI 会在发起或发布前校验文件。所有字段均必填,除非标注可选。正文为普通文本,网页不会执行其中代码。
|
|
4
|
+
|
|
5
|
+
## 文档与源码快照
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
type ReviewDocument = {
|
|
9
|
+
schemaVersion: 1
|
|
10
|
+
id: string
|
|
11
|
+
title: string
|
|
12
|
+
description: string
|
|
13
|
+
revision: string
|
|
14
|
+
snapshot: {
|
|
15
|
+
repository: string
|
|
16
|
+
commit: string
|
|
17
|
+
fingerprint: string
|
|
18
|
+
capturedAt: string
|
|
19
|
+
dirty: boolean
|
|
20
|
+
files?: { path: string; sha256: string }[]
|
|
21
|
+
}
|
|
22
|
+
coverage: { included: string[]; excluded: string[]; note: string }
|
|
23
|
+
scenes: Scene[]
|
|
24
|
+
nodes: Node[]
|
|
25
|
+
edges: Edge[]
|
|
26
|
+
comments: Comment[]
|
|
27
|
+
baseline?: Omit<ReviewDocument, 'baseline'>
|
|
28
|
+
}
|
|
29
|
+
type Scene = { id: string; title: string; description: string }
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- `id` 标识同一评审,跨轮保持;`revision` 标识当前逻辑内容,修改后更新,不能把轮次当作 Git commit。
|
|
33
|
+
- `snapshot.repository` 写真实来源仓库或工作区;`commit` 写当前 HEAD。无版本库时明确标注 `unversioned`,并在 `coverage.note` 解释来源。
|
|
34
|
+
- `capturedAt` 使用 ISO 日期;`dirty` 根据当前工作区事实填写,指纹必须包含本次覆盖的未提交内容。
|
|
35
|
+
- `snapshot.files` 建议显式列出受覆盖源码和文档,使用规范的工作区相对路径和文件原始字节的 SHA-256。将 `{path, sha256}[]` 按路径排序,对其 `JSON.stringify` 的 UTF-8 字节计算 SHA-256,作为 `snapshot.fingerprint`;CLI 固定采用 `sha256-path-content-v1`。未提供 `files` 时,以图内依据涉及的文件为清单;至少需要一个实际来源文件,设计评审也可列出真实设计文档。
|
|
36
|
+
- `coverage.included` 列出实际展示的行为;`excluded` 列出未覆盖路径和依赖;`note` 说明数据来源、摘要规则、未运行的验证等限制。展示成功不等于业务验收通过。
|
|
37
|
+
- 新评审的 `comments` 使用 `[]`。CLI `start --purpose TEXT [--context ID]` 将目的和上下文作为会话的 `scope` 保存,并清空输入包的旧批注;每次 start 生成独立 session ID。同一 `ReviewDocument.id` 可用于不同目的的独立评审,批注按 session 隔离。旧会话继续使用 `revise`,服务端会合并已有评论;保留从 `get` 取得的评论也可以,但不要自行重写原意见、回复 ID 或人工状态。
|
|
38
|
+
|
|
39
|
+
## 固定基线 B
|
|
40
|
+
|
|
41
|
+
B 保存本任务开始前的完整流程图,R1 保存本次修改后的第一次评审图。后续 R2、R3 继续相对 B 展示本任务的全部变化,也可相对上一轮评审核对新变化。B 与评审轮次分别保存。
|
|
42
|
+
|
|
43
|
+
已有改动时,准备 B 与当前实现两份图,从 R1 开始审查。需要先基于原系统讨论改造时,用原版图执行 `start before.logic-margin.json --source-root /workspace/original --baseline-review --purpose "在原系统上提出改造"`。此模式核验原版一次后自动固定为 B,并保存会话选项 `baselineReview: true`。它与独立 `--baseline-file` / `--baseline-source-root` 互斥,输入图从空批注开始。B 上保存的意见带 `basis: 'baseline'`,固定 B 图本身的 `comments` 仍为空。
|
|
44
|
+
|
|
45
|
+
基线评审模式的首个持久化轮次用于 B,第一次 `revise` 后的持久化轮次用于页面 R1,随后对应 R2、R3。CLI/MCP 的 `round` / `base_round` 始终使用服务返回的持久化轮次,响应保留 `baselineReview` 标识。Agent 收到 B 的意见后,先实际修改和验证业务源码,再核验并发布实现图。
|
|
46
|
+
|
|
47
|
+
生成代码改动评审时,先确定每个来源仓库的 Git 基线及本次覆盖范围,在独立旧树中实际读取原版代码,生成 B 的场景、规则、失败出口和源码依据,再从当前树生成 R1。多仓库任务分别冻结各仓库版本,在 `coverage.note` 记录基线和来源根目录的对应关系。文件摘要和补丁哈希用于核验材料身份,旧规则须据原版源码生成。
|
|
48
|
+
|
|
49
|
+
`baseline` 为可选的完整 `ReviewDocument`,其 `id` 与当前评审相同,`comments` 使用 `[]`,其中不含另一个 `baseline`。修改前后同一业务对象沿用同一节点和连线 ID。CLI 的 `start` 与 `verify` 可以分别读取两份图和两棵源码树。
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
start current.logic-margin.json --source-root /workspace/current --baseline-file before.logic-margin.json --baseline-source-root /workspace/before --purpose "核对本次修改"
|
|
53
|
+
verify current.logic-margin.json --source-root /workspace/current --baseline-file before.logic-margin.json --baseline-source-root /workspace/before
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
外部 `--baseline-file` 必须配合 `--baseline-source-root`。图中已嵌入 B 时,只需显式提供 `--baseline-source-root`。两份图分别核验文件字节、源码片段和指纹,任一核验失败均停止创建。合并后的评审文件仍受 10 MB 总大小限制。`verify` 的输出在当前图核验结果外增加 `baseline` 核验结果。正常 `revise` 省略 B 即继承固定基线,若携带 B,内容须与已保存的 B 相同。
|
|
57
|
+
|
|
58
|
+
旧会话缺 B 时,使用 `baseline SESSION_ID before.logic-margin.json --source-root /workspace/before`,或 MCP `attach_review_baseline`。它先核验旧树,再以已读版本补录到原会话,保留当前轮次;相同 B 可重复提交,其他 B 会产生冲突。补录前的旧固定意见包仍记录其提交时的材料。网页对缺 B 的旧会话显示“未提供修改前基线”。
|
|
59
|
+
|
|
60
|
+
## 步骤、状态和连线
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
type Node = {
|
|
64
|
+
id: string
|
|
65
|
+
sceneId: string
|
|
66
|
+
title: string
|
|
67
|
+
kind: 'action' | 'decision' | 'result' | 'failure'
|
|
68
|
+
summary: string
|
|
69
|
+
detail: string
|
|
70
|
+
input: string
|
|
71
|
+
output: string
|
|
72
|
+
position: { x: number; y: number }
|
|
73
|
+
evidence: {
|
|
74
|
+
path: string
|
|
75
|
+
symbol: string
|
|
76
|
+
startLine: number
|
|
77
|
+
endLine: number
|
|
78
|
+
digest: string
|
|
79
|
+
code?: string
|
|
80
|
+
}[]
|
|
81
|
+
}
|
|
82
|
+
type Edge = {
|
|
83
|
+
id: string
|
|
84
|
+
sceneId: string
|
|
85
|
+
source: string
|
|
86
|
+
target: string
|
|
87
|
+
label: string
|
|
88
|
+
detail: string
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
- 每种对象内的 ID 唯一;`sceneId` 必须引用现有场景,连线两端必须是同一场景内现有节点。业务对象改名或重排仍保留 ID,新的业务对象才新增 ID。
|
|
93
|
+
- `summary` 简述该步;`detail` 写规则与条件;`input/output` 写执行前后数据或资格变化。判断的分支在连线中标明条件,拒绝、失败和无副作用出口也应展示。
|
|
94
|
+
- 布局建议每个场景从上向下排列,普通相邻节点纵向间距约 180–240;分支横向错开。坐标仅用于排版,批注锚定 ID。
|
|
95
|
+
- `evidence.path` 使用来源工作区相对路径,且属于 `snapshot.files`;`symbol` 写真实函数或符号。行号从 1 开始且包含两端,`digest` 使用 UTF-8 片段的 SHA-256:将 CRLF 统一为 LF,按 `split('\n').slice(startLine - 1, endLine).join('\n')` 取片段后计算,不额外加入末尾换行。节点跨多个实现点时分别列依据。
|
|
96
|
+
- `evidence.code` 可选,保存上述行号范围的原文,用于节点内的代码对照。CLI 在来源核验全部成功后,从同一份已核验字节自动捕获,随基线或轮次快照保存;不要另行编写或概括代码。每段最多 100,000 字符、2,000 行,超限时保留源码位置和摘要但不嵌入文本。提供文本时必须有对应 `snapshot.files`,其行数和 SHA-256 必须匹配依据。旧文件缺少文本仍可导入,界面明确提示缺少快照,不读取当前工作区替代历史代码。
|
|
97
|
+
- 尚无源码的设计节点可用空 `evidence`,但应在节点说明和覆盖范围标明“设计/待实现”,不能伪造源码位置或摘要。
|
|
98
|
+
|
|
99
|
+
## 来源核验与新版发布
|
|
100
|
+
|
|
101
|
+
`verify <review.json> --source-root <workspace>` 核验实际文件、所有图内片段和指纹;只读,不替代理更新文件或判断业务语义。来源根目录默认当前工作目录;清单路径和符号链接必须留在该目录内,每文件最多 10 MB、总计 64 MB。
|
|
102
|
+
|
|
103
|
+
`start --source-root`、`baseline --source-root` 和 `revise --source-root` 会把已核验的源码片段连同清单保存到会话;`verify` 仅核验并输出清单,不重写输入文件。代码页签比较节点在两份固定快照中关联的片段;新增或取消源码关联不等于源文件中的代码新增或删除。
|
|
104
|
+
|
|
105
|
+
旧会话的固定基线若未保存 `code`,需从对应版本核验源码,重新发起包含代码快照的评审;重新执行 `baseline` 不能补写或替换已有固定基线。
|
|
106
|
+
|
|
107
|
+
`revise SESSION_ID <review.json> --base-round NUMBER --summary TEXT` 自动再核验,将核验后的 `files`、发布基线及说明保存到本轮快照。`NUMBER` 必须是代理从 `wait` 或 `get` 核对并实际处理的轮次;当前轮次已变化时重新读图处理,保留旧草稿的真实基线。逻辑未变时必填 `--unchanged-reason TEXT`,明确仅改依据、格式或说明的范围。
|
|
108
|
+
|
|
109
|
+
最终回应使用 `--replies-file <JSON file>` 随新版原子发布,格式如下;每个 `commentId` 必须引用已有线程:
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
[
|
|
113
|
+
{ "commentId": "原批注 ID", "body": "实际修改、验证结果及剩余限制。" }
|
|
114
|
+
]
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
发布失败不会生成下一轮或部分最终回应。独立 `reply` 仅用于处理中交流;新版发布前,人工也不能确认解决或整轮通过。
|
|
118
|
+
|
|
119
|
+
## 批注由会话维护
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
type Comment = {
|
|
123
|
+
id: string
|
|
124
|
+
body: string
|
|
125
|
+
targets: { kind: 'node' | 'edge' | 'scene' | 'review'; id: string }[]
|
|
126
|
+
status: 'open' | 'resolved' | 'needs-review'
|
|
127
|
+
createdAt: string
|
|
128
|
+
revision: string
|
|
129
|
+
reviewedRevision?: string
|
|
130
|
+
originRound?: number
|
|
131
|
+
reviewedRound?: number
|
|
132
|
+
basis?: 'baseline'
|
|
133
|
+
replies?: {
|
|
134
|
+
id: string
|
|
135
|
+
author: 'reviewer' | 'agent'
|
|
136
|
+
body: string
|
|
137
|
+
createdAt: string
|
|
138
|
+
revision: string
|
|
139
|
+
round?: number
|
|
140
|
+
}[]
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
旧批注的 `revision` 与 `originRound` 保留提出时版本及轮次,人工复核记录在 `reviewedRevision` / `reviewedRound`,回复的 `round` 记录回应所属发布;这些字段由会话维护。同一 revision 可以有多轮说明或复核,定位以轮次快照为准。Agent 随 `revise` 发布最终回应,评审者在当前图确认解决或重新打开。新版移除目标时保留旧批注,可回到提出时的只读图查看原位置,不按屏幕位置猜测替代目标。
|
|
145
|
+
|
|
146
|
+
## 容量与校验
|
|
147
|
+
|
|
148
|
+
至少一个场景;场景、节点、连线、批注、回复及轮次不设固定业务条数上限。ID 为非空文本且最多 200 字符;标题和连线标签最多 200 字符,节点摘要最多 2000,说明、输入输出和批注正文最多 10000。坐标必须为有限数,绝对值不超过 10000000;源码行号为正整数,结束行不小于起始行。轮次为正的安全整数。会话 `scope.context` 为非空文本且最多 1000 字符,`scope.purpose` 为非空文本且最多 10000 字符。
|
|
149
|
+
|
|
150
|
+
CLI 单次读入的评审 JSON 与最终回应 JSON 各最多 10 MB,作为一次输入的资源边界;服务端累计评论、快照及历史不按此阈值裁剪或停止增长。`revise` 的输入可只包含新图及新增回应,旧线程由会话合并,避免重复传入累计评论。
|
|
151
|
+
|
|
152
|
+
非法引用、重复 ID、缺字段或超限会拒绝载入;先修正本地评审文件再重试。保持原会话和评论,不通过创建空会话绕过校验。
|
|
153
|
+
|
|
154
|
+
## 会话生命周期
|
|
155
|
+
|
|
156
|
+
`scope` 和 `cleanupPolicy` 属于会话,不写进 `ReviewDocument`。默认 `cleanupPolicy: 'manual'`;`start --cleanup-on-approval` 保存 `'archive-on-approval'`,由代理等待命令在输出最终通过结果后归档。归档保存完整图、批注、轮次及历史,可用原 session ID 恢复;永久删除同时移除该 ID 的活动与归档数据。归档、恢复及删除均核对已读 `version`,版本冲突时保留数据。
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
$ErrorActionPreference = 'Stop'
|
|
2
|
+
|
|
3
|
+
function Assert-NativeVersion([string] $Path, [string] $Version) {
|
|
4
|
+
$actual = (& $Path --version) -join "`n"
|
|
5
|
+
if ($LASTEXITCODE -ne 0) { throw "Cannot read native executable version: $Path" }
|
|
6
|
+
if ($actual -ne $Version) { throw "Native executable version $actual does not match plugin ${Version}: $Path" }
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
# Keep UTF-8 review JSON intact through Windows PowerShell 5.1 redirected output.
|
|
11
|
+
[Console]::OutputEncoding = [Text.UTF8Encoding]::new($false)
|
|
12
|
+
$scriptPath = $PSCommandPath
|
|
13
|
+
for ($links = 0; ; $links++) {
|
|
14
|
+
$item = Get-Item -LiteralPath $scriptPath
|
|
15
|
+
if ($item.LinkType -ne 'SymbolicLink') { break }
|
|
16
|
+
if ($links -ge 40) { throw 'Too many launcher symlinks.' }
|
|
17
|
+
$target = @($item.Target)[0]
|
|
18
|
+
if ([IO.Path]::IsPathRooted($target)) { $scriptPath = $target }
|
|
19
|
+
else { $scriptPath = Join-Path (Split-Path -Parent $item.FullName) $target }
|
|
20
|
+
}
|
|
21
|
+
$directory = Split-Path -Parent $item.FullName
|
|
22
|
+
$manifest = Join-Path $directory '../../../plugin.json'
|
|
23
|
+
$version = (Get-Content -LiteralPath $manifest -Raw | ConvertFrom-Json).version
|
|
24
|
+
if ($version -notmatch '\A[0-9]+\.[0-9]+\.[0-9]+\z') { throw 'Plugin manifest must pin a version X.Y.Z.' }
|
|
25
|
+
$binary = Join-Path $directory 'logic-margin.exe'
|
|
26
|
+
if (-not [IO.File]::Exists($binary)) {
|
|
27
|
+
$machine = $env:PROCESSOR_ARCHITEW6432
|
|
28
|
+
if (-not $machine) { $machine = $env:PROCESSOR_ARCHITECTURE }
|
|
29
|
+
if ([Environment]::OSVersion.Platform -ne [PlatformID]::Win32NT -or $machine -ne 'AMD64') {
|
|
30
|
+
throw "Unsupported platform: $([Environment]::OSVersion.Platform)/$machine (requires Windows x64)."
|
|
31
|
+
}
|
|
32
|
+
if (-not $env:LOCALAPPDATA) { throw 'LOCALAPPDATA must be set.' }
|
|
33
|
+
$cache = Join-Path $env:LOCALAPPDATA "logic-margin/native/$version/win32-x64"
|
|
34
|
+
$binary = Join-Path $cache 'logic-margin.exe'
|
|
35
|
+
if (-not [IO.File]::Exists($binary)) {
|
|
36
|
+
[IO.Directory]::CreateDirectory($cache) | Out-Null
|
|
37
|
+
$temporary = Join-Path $cache ('.download.' + [Guid]::NewGuid().ToString('N'))
|
|
38
|
+
[IO.Directory]::CreateDirectory($temporary) | Out-Null
|
|
39
|
+
$previousTls = [Net.ServicePointManager]::SecurityProtocol
|
|
40
|
+
try {
|
|
41
|
+
$package = "logic-margin-agent-$version-win32-x64"
|
|
42
|
+
$asset = "$package.zip"
|
|
43
|
+
$url = "https://github.com/SunneeYang/LogicMargin/releases/download/v$version/$asset"
|
|
44
|
+
$zip = Join-Path $temporary $asset
|
|
45
|
+
$checksum = Join-Path $temporary 'checksum'
|
|
46
|
+
[Console]::Error.WriteLine("LogicMargin: Downloading $asset")
|
|
47
|
+
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
|
48
|
+
Invoke-WebRequest -Uri $url -OutFile $zip -UseBasicParsing -TimeoutSec 120 -MaximumRedirection 5
|
|
49
|
+
Invoke-WebRequest -Uri "$url.sha256" -OutFile $checksum -UseBasicParsing -TimeoutSec 120 -MaximumRedirection 5
|
|
50
|
+
$record = Get-Content -LiteralPath $checksum -Raw
|
|
51
|
+
if ($record -notmatch ('\A([a-fA-F0-9]{64})[ \t]+' + [regex]::Escape($asset) + '\r?\n?\z')) {
|
|
52
|
+
throw 'Invalid release SHA-256 file.'
|
|
53
|
+
}
|
|
54
|
+
$expected = $Matches[1]
|
|
55
|
+
$hasher = [Security.Cryptography.SHA256]::Create()
|
|
56
|
+
try {
|
|
57
|
+
$archiveStream = [IO.File]::OpenRead($zip)
|
|
58
|
+
try { $actual = [BitConverter]::ToString($hasher.ComputeHash($archiveStream)).Replace('-', '') }
|
|
59
|
+
finally { $archiveStream.Dispose() }
|
|
60
|
+
} finally { $hasher.Dispose() }
|
|
61
|
+
if ($actual -ne $expected) { throw "SHA-256 mismatch for $asset" }
|
|
62
|
+
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
|
63
|
+
$archive = [IO.Compression.ZipFile]::OpenRead($zip)
|
|
64
|
+
$candidate = Join-Path $temporary 'logic-margin.exe'
|
|
65
|
+
try {
|
|
66
|
+
$entry = $archive.GetEntry("$package/plugins/logic-margin/skills/logic-margin/scripts/logic-margin.exe")
|
|
67
|
+
if ($null -eq $entry) { throw "Missing executable in $asset" }
|
|
68
|
+
$source = $entry.Open()
|
|
69
|
+
try {
|
|
70
|
+
$output = [IO.File]::Open($candidate, [IO.FileMode]::CreateNew, [IO.FileAccess]::Write, [IO.FileShare]::None)
|
|
71
|
+
try { $source.CopyTo($output) } finally { $output.Dispose() }
|
|
72
|
+
} finally { $source.Dispose() }
|
|
73
|
+
} finally { $archive.Dispose() }
|
|
74
|
+
Assert-NativeVersion $candidate $version
|
|
75
|
+
try { [IO.File]::Move($candidate, $binary) }
|
|
76
|
+
catch { if (-not [IO.File]::Exists($binary)) { throw } }
|
|
77
|
+
} finally {
|
|
78
|
+
[Net.ServicePointManager]::SecurityProtocol = $previousTls
|
|
79
|
+
[IO.Directory]::Delete($temporary, $true)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
Assert-NativeVersion $binary $version
|
|
84
|
+
$start = New-Object Diagnostics.ProcessStartInfo
|
|
85
|
+
$start.FileName = $binary
|
|
86
|
+
$start.UseShellExecute = $false
|
|
87
|
+
$start.WorkingDirectory = (Get-Location).ProviderPath
|
|
88
|
+
$start.RedirectStandardOutput = $true
|
|
89
|
+
$start.RedirectStandardError = $true
|
|
90
|
+
$start.StandardOutputEncoding = [Text.Encoding]::UTF8
|
|
91
|
+
$start.StandardErrorEncoding = [Text.Encoding]::UTF8
|
|
92
|
+
# Windows PowerShell 5.1 drops empty/quoted arguments with direct native splatting.
|
|
93
|
+
$start.Arguments = (($args | ForEach-Object { '"' + ([string]$_ -replace '(\\*)"', '$1$1\"' -replace '(\\+)$', '$1$1') + '"' }) -join ' ')
|
|
94
|
+
$process = [Diagnostics.Process]::Start($start)
|
|
95
|
+
$stderr = $process.StandardError.ReadToEndAsync()
|
|
96
|
+
while ($null -ne ($line = $process.StandardOutput.ReadLine())) { Write-Output $line }
|
|
97
|
+
$process.WaitForExit()
|
|
98
|
+
[Console]::Error.Write($stderr.GetAwaiter().GetResult())
|
|
99
|
+
$code = $process.ExitCode
|
|
100
|
+
$process.Dispose()
|
|
101
|
+
exit $code
|
|
102
|
+
} catch {
|
|
103
|
+
[Console]::Error.WriteLine('LogicMargin: ' + $_.Exception.Message)
|
|
104
|
+
exit 1
|
|
105
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -eu
|
|
3
|
+
|
|
4
|
+
fail() { printf 'LogicMargin: %s\n' "$*" >&2; exit 1; }
|
|
5
|
+
|
|
6
|
+
script=$0
|
|
7
|
+
case "$script" in */*) ;; *) script=$(command -v "$script") || fail 'Cannot locate launcher.' ;; esac
|
|
8
|
+
links=0
|
|
9
|
+
while [ -L "$script" ]; do
|
|
10
|
+
links=$((links + 1))
|
|
11
|
+
[ "$links" -le 40 ] || fail 'Too many launcher symlinks.'
|
|
12
|
+
directory=$(CDPATH= cd -P "$(dirname "$script")" && pwd) || fail 'Cannot resolve launcher directory.'
|
|
13
|
+
link=$(readlink "$script") || fail 'Cannot resolve launcher symlink.'
|
|
14
|
+
case "$link" in /*) script=$link ;; *) script=$directory/$link ;; esac
|
|
15
|
+
done
|
|
16
|
+
directory=$(CDPATH= cd -P "$(dirname "$script")" && pwd) || fail 'Cannot resolve launcher directory.'
|
|
17
|
+
manifest=$directory/../../../plugin.json
|
|
18
|
+
[ -f "$manifest" ] || fail "Missing plugin manifest: $manifest"
|
|
19
|
+
version=$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\)".*/\1/p' "$manifest")
|
|
20
|
+
case "$version" in ''|*[!0-9.]*) fail 'Plugin manifest must pin a version X.Y.Z.' ;; esac
|
|
21
|
+
|
|
22
|
+
check_version() {
|
|
23
|
+
actual_version=$("$1" --version) || fail "Cannot read native executable version: $1"
|
|
24
|
+
[ "$actual_version" = "$version" ] || fail "Native executable version $actual_version does not match plugin $version: $1"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
binary=$directory/logic-margin
|
|
28
|
+
if [ -e "$binary" ]; then
|
|
29
|
+
[ -f "$binary" ] && [ -x "$binary" ] || fail "Native executable is not executable: $binary"
|
|
30
|
+
check_version "$binary"
|
|
31
|
+
exec "$binary" "$@"
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
system=$(uname -s)
|
|
35
|
+
machine=$(uname -m)
|
|
36
|
+
case "$system" in Darwin) platform=darwin ;; Linux) platform=linux ;; *) fail "Unsupported platform: $system/$machine" ;; esac
|
|
37
|
+
case "$machine" in arm64|aarch64) arch=arm64 ;; x86_64|amd64) arch=x64 ;; *) fail "Unsupported platform: $system/$machine" ;; esac
|
|
38
|
+
target=$platform-$arch
|
|
39
|
+
cache_base=${XDG_CACHE_HOME:-}
|
|
40
|
+
if [ -z "$cache_base" ]; then
|
|
41
|
+
[ -n "${HOME:-}" ] || fail 'HOME or XDG_CACHE_HOME must be set.'
|
|
42
|
+
case "$platform" in darwin) cache_base=$HOME/Library/Caches ;; *) cache_base=$HOME/.cache ;; esac
|
|
43
|
+
fi
|
|
44
|
+
cache=$cache_base/logic-margin/native/$version/$target
|
|
45
|
+
binary=$cache/logic-margin
|
|
46
|
+
if [ ! -e "$binary" ]; then
|
|
47
|
+
for dependency in curl unzip; do
|
|
48
|
+
command -v "$dependency" >/dev/null 2>&1 || fail "Required command is missing: $dependency"
|
|
49
|
+
done
|
|
50
|
+
if command -v sha256sum >/dev/null 2>&1; then
|
|
51
|
+
checksum_command=sha256sum
|
|
52
|
+
elif command -v shasum >/dev/null 2>&1; then
|
|
53
|
+
checksum_command=shasum
|
|
54
|
+
else
|
|
55
|
+
fail 'Required command is missing: sha256sum or shasum'
|
|
56
|
+
fi
|
|
57
|
+
previous_umask=$(umask)
|
|
58
|
+
umask 077
|
|
59
|
+
mkdir -p "$cache" || fail "Cannot create native cache: $cache"
|
|
60
|
+
temporary=$(mktemp -d "$cache/.download.XXXXXXXX") || fail 'Cannot create download directory.'
|
|
61
|
+
trap 'rm -rf "$temporary"' 0
|
|
62
|
+
trap 'exit 129' HUP
|
|
63
|
+
trap 'exit 130' INT
|
|
64
|
+
trap 'exit 143' TERM
|
|
65
|
+
package=logic-margin-agent-$version-$target
|
|
66
|
+
asset=$package.zip
|
|
67
|
+
url=https://github.com/SunneeYang/LogicMargin/releases/download/v$version/$asset
|
|
68
|
+
printf 'LogicMargin: Downloading %s\n' "$asset" >&2
|
|
69
|
+
curl --fail --location --silent --show-error --proto '=https' --proto-redir '=https' --connect-timeout 10 --max-time 120 --output "$temporary/$asset" "$url" || fail "Download failed: $url"
|
|
70
|
+
curl --fail --location --silent --show-error --proto '=https' --proto-redir '=https' --connect-timeout 10 --max-time 120 --output "$temporary/checksum" "$url.sha256" || fail "Download failed: $url.sha256"
|
|
71
|
+
read -r expected checksum_name < "$temporary/checksum" || fail 'Invalid release SHA-256 file.'
|
|
72
|
+
case "$expected" in ''|*[!0-9a-f]*) fail 'Invalid release SHA-256 file.' ;; esac
|
|
73
|
+
[ "${#expected}" -eq 64 ] && [ "$checksum_name" = "$asset" ] || fail 'Invalid release SHA-256 file.'
|
|
74
|
+
if [ "$checksum_command" = shasum ]; then
|
|
75
|
+
actual=$(shasum -a 256 "$temporary/$asset") || fail 'Cannot calculate SHA-256.'
|
|
76
|
+
else
|
|
77
|
+
actual=$(sha256sum "$temporary/$asset") || fail 'Cannot calculate SHA-256.'
|
|
78
|
+
fi
|
|
79
|
+
[ "${actual%% *}" = "$expected" ] || fail "SHA-256 mismatch for $asset"
|
|
80
|
+
entry=$package/plugins/logic-margin/skills/logic-margin/scripts/logic-margin
|
|
81
|
+
unzip -p "$temporary/$asset" "$entry" > "$temporary/logic-margin" || fail "Missing or invalid executable in $asset"
|
|
82
|
+
chmod 700 "$temporary/logic-margin" || fail 'Cannot make native executable runnable.'
|
|
83
|
+
check_version "$temporary/logic-margin"
|
|
84
|
+
mv -f "$temporary/logic-margin" "$binary" || fail "Cannot install native executable: $binary"
|
|
85
|
+
rm -rf "$temporary"
|
|
86
|
+
trap - 0 HUP INT TERM
|
|
87
|
+
umask "$previous_umask"
|
|
88
|
+
fi
|
|
89
|
+
[ -f "$binary" ] && [ -x "$binary" ] || fail "Cached native executable is not executable: $binary"
|
|
90
|
+
check_version "$binary"
|
|
91
|
+
exec "$binary" "$@"
|