## 删了什么
**本地 MCP 服务器**(工具面已内置于网关 `POST /api/v1/mcp`,见 457d160):
mcp/server.mjs stdio JSON-RPC 入口
lib/tools.mjs 11 个工具(手抄网关语义 —— 已抓到两次抄错)
lib/mcp-rpc.mjs 手写协议层
test/{tools,mcp-rpc,generic-mcp}.test.mjs
**执行门禁整条链**(用户裁定:直接移除):
lib/action-tools.mjs run_command / write_file
lib/approval.mjs 授权判定
lib/grants-file.mjs 「一直同意」跨进程持久化
lib/hook-policy.mjs 档位判定
hooks/permission.mjs PermissionRequest 钩子
hooks/hooks.json 钩子注册
test/{action-tools,approval,grants-file,hook-policy}.test.mjs
test/manual/{gate-e2e.py,permission-e2e.mjs,gate-e2e-evidence.json}
## 为什么执行门禁可以整条删,而不是留着
`run_command` / `write_file` 的门禁是**双进程审批**设计:MCP 进程问人、
ZCode 钩子进程等回答、中间靠落盘授权表对齐。三样都依赖**本地 MCP 进程**。
进程没了之后:
没有任何代码装载 buildActionTools ← 实测确认(只剩测试在测它)
也就是说它已经是死代码,而死代码 + 它的判据会让人误以为「这个平台有执行面」。
留着比删掉更危险。
本平台现在的姿态是**失败关闭**:平台自带 32 项危险工具被禁用,
AgentMail 侧不提供任何执行类工具 ⇒ 模型没有执行面。
## detectModeEnforcement 重写
它原本有三条依据,现在只剩一条还成立:
1. ~~平台 PermissionRequest 钩子~~ —— 目录整个删了。**留着「钩子是否注册」
的判据只会说谎。**
2. ~~我们自己的门禁~~ —— 删了。
3. ✅ `--disallowed-tools` 禁用清单 —— 仍在,且现在是**唯一**那道。
报 `native` 的含义随之收窄为「该档位真的**没有执行面**」,而不是以前那个
「有人会来问」。降级路径(清单被清空 ⇒ advisory)仍有效,实测:
正常配置 → native | 平台自带危险工具已禁用 32 项…⇒ 模型无任何执行面
清单清空 → advisory | 禁用清单自检未通过…禁用清单就是唯一那道
## 清单与 package.json
`.zcode-plugin/plugin.json` 去掉 `mcpServers` 与 `hooks`(两者的目标都已不存在)。
`package.json` 去掉 `main` 与 `verify`(已无本地入口)。
## 误删与自查
删 `test/permission-grants.test.mjs` 时**误删了一个仍在使用的共用库的测试**
—— `lib/permission-grants.js` 四方同源,dsh / opencode / pi 都还在用。
`deploy/check-shared-libs.sh` 立刻报「共用测试缺失」把它抓出来,已恢复。
若没有那道检查,这会是一个静默的覆盖损失。
## 验证
node --test 'test/*.test.mjs' 293/293 绿(原 352,删掉 59 格死代码判据)
deploy/check-shared-libs.sh 四方同源 rc=0
detectModeEnforcement 实测 native / advisory 两条路径都对
## 后续
本目录现在只剩**邮件驱动**(SSE 订阅 → 起一轮 → 回信)与共用库。
若将来要在 zcode 侧恢复执行能力,需要重新设计门禁 —— 现有形状不能复用,
因为它的双进程模型随本地 MCP 进程一起消失了。
70 lines
2.8 KiB
JavaScript
70 lines
2.8 KiB
JavaScript
/**
|
||
* 入口判断的测试 —— 这个缺陷只在**部署形态**下出现,所以必须专门钉住。
|
||
*
|
||
* 实测经过:生产布局是 `current` 软链,入口用
|
||
* `import.meta.url === 'file://' + process.argv[1]` 判断是否直接执行 ——
|
||
* `import.meta.url` 是解析过软链的真实路径,argv 是软链路径,两者不等,
|
||
* 于是 `main()` 从不执行:**没有输出、没有报错、退出码 0**。
|
||
*
|
||
* 从仓库路径跑(无软链)完全正常,所以本地怎么试都发现不了。
|
||
*/
|
||
|
||
import { test } from 'node:test';
|
||
import assert from 'node:assert/strict';
|
||
import { mkdtemp, mkdir, writeFile, symlink, rm } from 'node:fs/promises';
|
||
import { tmpdir } from 'node:os';
|
||
import { join } from 'node:path';
|
||
import { pathToFileURL } from 'node:url';
|
||
import { isMainModule } from '../lib/is-main.mjs';
|
||
|
||
test('同一个文件的真实路径 → 是入口', () => {
|
||
const url = pathToFileURL('/etc/hostname').href;
|
||
assert.equal(isMainModule(url, '/etc/hostname'), true);
|
||
});
|
||
|
||
test('★ 通过软链指向自己 → 仍是入口(生产布局就是软链)', async () => {
|
||
const dir = await mkdtemp(join(tmpdir(), 'zc-is-main-'));
|
||
try {
|
||
const real = join(dir, 'real.mjs');
|
||
const link = join(dir, 'current.mjs');
|
||
await writeFile(real, '// x\n', 'utf8');
|
||
await symlink(real, link);
|
||
const url = pathToFileURL(real).href;
|
||
assert.equal(
|
||
isMainModule(url, link),
|
||
true,
|
||
'软链路径必须被认成同一个文件 —— 否则快照部署下入口静默不执行'
|
||
);
|
||
} finally {
|
||
await rm(dir, { recursive: true, force: true });
|
||
}
|
||
});
|
||
|
||
test('★ 目录软链(current → <时间戳>)下的完整路径同样成立', async () => {
|
||
// 生产的软链在**目录**这一层:/opt/.../<name>/current/src/index.mjs
|
||
const dir = await mkdtemp(join(tmpdir(), 'zc-is-main-d-'));
|
||
try {
|
||
await mkdir(join(dir, '20260101-000000', 'src'), { recursive: true });
|
||
const real = join(dir, "20260101-000000", "src", "index.mjs");
|
||
await writeFile(real, '// x\n', 'utf8');
|
||
await symlink('20260101-000000', join(dir, 'current'));
|
||
assert.equal(
|
||
isMainModule(pathToFileURL(real).href, join(dir, "current", "src", "index.mjs")),
|
||
true
|
||
);
|
||
} finally {
|
||
await rm(dir, { recursive: true, force: true });
|
||
}
|
||
});
|
||
|
||
test('★ 反向对照:别的文件不是入口', () => {
|
||
assert.equal(isMainModule(pathToFileURL('/etc/hostname').href, '/etc/hosts'), false);
|
||
});
|
||
|
||
test('缺失的 argv 或文件不存在 → 保守判否(不误跑一遍)', () => {
|
||
assert.equal(isMainModule(pathToFileURL('/etc/hostname').href, ''), false);
|
||
assert.equal(isMainModule(pathToFileURL('/etc/hostname').href, undefined), false);
|
||
assert.equal(isMainModule(pathToFileURL('/etc/hostname').href, '/no/such/file/xyz'), false);
|
||
assert.equal(isMainModule('', '/etc/hostname'), false);
|
||
});
|