@tomflow/chatloop-tools 0.4.5 → 0.4.6

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
@@ -1,16 +1,31 @@
1
1
  # @tomflow/chatloop-tools — Chat Loop 工具包
2
2
 
3
- **v0.4.5 · npm 安装包 · Node.js >=20 · 当前验证平台 macOS**
3
+ **v0.4.6 · npm 安装包 · Node.js >=20 · 当前验证平台 macOS**
4
4
 
5
- 本包是**本地 CLI + 统一 stdio MCP Gateway**:一套命令管理多个工具模块的启动、停止、重启、状态;一个 MCP 入口统一对外暴露各模块的工具 API。目前代理 **Desktop Commander 0.2.44** 原有 26 个工具,新增 **Chat Loop Extension** 四个专用 Action CLI 工具(总计 30 个 MCP 工具),Repomix 尚未接入。它**不是** ChatGPT Plugin、Secure MCP Tunnel、tunnel-client 或 Chrome 扩展,也不会替用户安装这些组件。
5
+ 本包是**本地 CLI + 统一 stdio MCP Gateway**:一套命令管理多个工具模块的启动、停止、重启、状态;一个 MCP 入口统一对外暴露各模块的工具 API。目前代理 **Desktop Commander 0.2.44** 原有 26 个工具,新增 **Chat Loop Extension** 四个专用 Action CLI 工具(总计 30 个 MCP 工具),Repomix 尚未接入。它**不是** ChatGPT Plugin、Secure MCP Tunnel 或 Chrome 扩展;但 v0.4.6 **随 npm 自动安装一份 Chat Loop 私有的对应平台 tunnel-client 二进制依赖**,不会使用 Local Dev/Homebrew 上的程序,也不包含任何用户凭据。
6
6
 
7
7
  > **给人类和 AI 的 API 合同**:此 README 随 npm 包分发,可独立阅读;对外的稳定使用入口是 `chatloop-tools` CLI(兼容别名 `chat-loop-local`)和 `chatloop-tools mcp` 提供的 MCP 协议。**无承诺的 JavaScript import/exports API**,不要依赖 `lib/` 内部模块。研发架构文档在仓库 `docs/17-local-tools-multi-tool-plan.md`,不随 npm 包发行。
8
8
 
9
+ ## Chat Loop 专属 tunnel-client:打包内置,不依赖 Local Dev
10
+
11
+ v0.4.6 通过自身独立的 npm 平台二进制依赖分发 OpenAI 官方 [tunnel-client v0.0.19](https://github.com/openai/tunnel-client/releases/tag/v0.0.19) 的 macOS Intel(x64)/Apple Silicon(arm64) **完整发行二进制与 cloudflared 同伴程序**分别封装在同一 `@tomflow` scope 的 `@tomflow/chatloop-tunnel-darwin-{x64,arm64}@0.0.19` 平台子包,主包用 `optionalDependencies` 由 npm **自动只安装匹配当前 OS/CPU 的包**。这仍属于 Chat Loop Tools 的内部依赖,不安装到 PATH、Homebrew 或 Local Dev。两份官方 ZIP 均按上游 SHA256SUMS 验证;将许可证、NOTICE、第三方许可证、SPDX 和二进制 SHA256 溯源一并保留。启动只调用本包自身的绝对路径,不搜索 PATH、不依赖本机 Homebrew 或 Local Dev。
12
+
13
+ ```bash
14
+ npx --yes --package=@tomflow/chatloop-tools@latest chatloop-tools tunnel doctor
15
+ npx --yes --package=@tomflow/chatloop-tools@latest chatloop-tools tunnel --version
16
+ # 用户完成专属 Tunnel 的账号授权、配置后,前台运行:
17
+ npx --yes --package=@tomflow/chatloop-tools@latest chatloop-tools tunnel run
18
+ ```
19
+
20
+ `tunnel doctor` 校验当前架构的 tunnel-client 和 cloudflared 哈希,报告 `BUNDLED_READY` 不代表连接已认证;`tunnel run` 默认使用**本产品自己的** `~/.chat-loop/tunnel/profiles` 和 `chat-loop` profile,与 Local Dev 完全分离;所有凭据由用户自行配置,不装入 npm。CLI 目前提供**前台** `tunnel run`,尚未将 Tunnel 进程接入 `start/stop/restart` 的统一 Supervisor 生命周期;本地真实程序的 `--version` 与 SHA-256 已验收,但云端握手需要用户自己的授权。
21
+
22
+ **限制**:当前内置二进制仅适用 macOS Intel/Apple Silicon;其他操作系统明确失败而不回退系统 PATH。官方 macOS 发布二进制未签名;当前 Mac 可运行,Gatekeeper 单独检查拒绝,**其他用户机器的安装/公证/首次执行尚未验收**,不能宣称所有 macOS 用户零配置成功。
23
+
9
24
  ## 1. 可用范围与安全前提
10
25
 
11
26
  - **已经验证**:仓库内 CLI 生命周期、真实 Desktop Commander MCP 握手和只读调用、26 项工具发现、单工具故障隔离、Gateway 断线重连;全部在本机测试或隔离状态目录内完成。
12
27
  - **已验证(2026-11-05)**:v0.4.0 本地 tarball 在仓库外 macOS 临时目录用 npm 安装(`--ignore-scripts`),安装后的 `doctor/status/start/stop` 正常;MCP initialize、26 工具列表与 `read_file` 调用读回临时文件均通过。未改现有 Local Dev/Tunnel。
13
- - **未验证 / 不能宣称可正式上线**:用户自有 Tunnel → ChatGPT 完整联通、新版 2026 MCP 客户端、Windows/Linux、长任务取消、用户目录/命令授权边界、无 `--ignore-scripts` 的完整安装差异、npm/Plugin 公共发布。
28
+ - **已验收历史与当前缺口要区分**:旧 v0.4.3 通过用户独立 Tunnel → ChatGPT 的 26 工具发现和真实只读 tools/call;正式 npm v0.4.5 已发布、npx latest 本地 MCP 30 工具发现通过,但**当前专用 Tunnel 因安全工具阻断重连,处于 STOPPED**,v0.4.5 的远端 tools/call **仍未验收**;但真实 macOS Chrome 0.3.29 + Native Host + 本地 MCP tools/call 的 Text/DOM/PNG/Reload **已通过**,并在 Reload 后正式 Audit 复核版本/Owner 未变化。Windows/Linux、新版 MCP 2026 协议、长任务取消、最小权限及公开 Chat Loop 产品 Plugin 发布也未验收。
14
29
  - **高风险权限**:Desktop Commander 在独立 `HOME` 下运行,但仍继承**当前操作系统用户的文件、进程与命令权限**;这不是文件系统沙箱。只在获用户明确授权的机器上运行,勿以管理员权限启动,勿向不可信客户端暴露;生产级目录白名单/命令审批**尚未实现**。
15
30
 
16
31
  ## 2. 安装及入口
@@ -45,7 +60,7 @@ npx --yes --package=@tomflow/chatloop-tools@latest chatloop-tools mcp
45
60
  cd packages/local-tools
46
61
  npm pack
47
62
  # 在仓库外一个全新的目录:
48
- npm install /absolute/path/to/tomflow-chatloop-tools-0.4.5.tgz
63
+ npm install /absolute/path/to/tomflow-chatloop-tools-0.4.6.tgz
49
64
  ./node_modules/.bin/chat-loop-local doctor
50
65
  ./node_modules/.bin/chat-loop-local status
51
66
  ```
@@ -190,7 +205,7 @@ npm install /absolute/path/to/tomflow-chatloop-tools-0.4.5.tgz
190
205
 
191
206
  ## 7. 本机验证与发布闸门
192
207
 
193
- 在 Chat Loop 仓库根目录:
208
+ 在 Chat Loop 仓库根目录(根 `npm test` 限制测试文件并发为 2,避免共享资源负载使真实 Desktop Commander 生命周期超时;仍保留全部 184 个测试用例):
194
209
 
195
210
  ```bash
196
211
  npm run check
package/bin/cli.mjs CHANGED
@@ -8,6 +8,7 @@ import {fileURLToPath} from 'node:url';
8
8
  import {CONTRACT,ACTIONS,registry,ensurePrivateDir,paths,
9
9
  safeFallback,recoverStaleLock,exitCode,talk} from '../lib/control.mjs';
10
10
  import {runGateway} from '../lib/gateway.mjs';
11
+ import {tunnelDoctor,runTunnel} from '../lib/tunnel.mjs';
11
12
 
12
13
  const require=createRequire(import.meta.url);
13
14
  const command=process.argv[2]??'doctor';
@@ -96,10 +97,24 @@ if(command==='mcp'){
96
97
  else if(command==='doctor'){
97
98
  if(extra.length){emit(fail('INVALID_ARGUMENT'));process.exitCode=2;}
98
99
  else{const snapshot=inspect();emit({contract:'chat-loop.local-tools.v1',command,...snapshot});if(snapshot.status!=='LOCAL_READY')process.exitCode=1;}
100
+ }else if(command==='tunnel'){
101
+ if(extra.length===1&&extra[0]==='doctor'){
102
+ const result=tunnelDoctor();
103
+ emit({contract:'chat-loop.local-tools.v1',command,...result});
104
+ if(result.status!=='BUNDLED_READY')process.exitCode=1;
105
+ }else if(!extra.length){
106
+ emit({contract:'chat-loop.local-tools.v1',command,status:'FAILED',
107
+ code:'INVALID_ARGUMENT',usage:'tunnel doctor | tunnel --version | tunnel run [args]'});
108
+ process.exitCode=2;
109
+ }else{
110
+ try{await runTunnel([...extra]);}
111
+ catch(e){emit({contract:'chat-loop.local-tools.v1',command,status:'FAILED',
112
+ code:e.code||'TUNNEL_EXEC_FAILED'});process.exitCode=1;}
113
+ }
99
114
  }else if(command==='capabilities'){
100
115
  if(extra.length){emit(fail('INVALID_ARGUMENT'));process.exitCode=2;}
101
116
  else emit({contract:'chat-loop.local-tools.v1',command,status:'OK',
102
- implemented:['capabilities','doctor','mcp','start','stop','restart','status'],
117
+ implemented:['capabilities','doctor','mcp','start','stop','restart','status','tunnel'],
103
118
  lifecycle:'SUPERVISOR_MCP_GATEWAY',mcpTools:'Namespaced tool catalog',
104
- tunnel:'SEPARATE_USER_RESOURCE'});
119
+ tunnel:'BUNDLED_ISOLATED_CLI'});
105
120
  }else{emit({contract:'chat-loop.local-tools.v1',command,status:'FAILED',code:'UNKNOWN_COMMAND'});process.exitCode=1;}
package/lib/gateway.mjs CHANGED
@@ -46,7 +46,7 @@ export function runGateway(){
46
46
  return reject(m.id,-32602,'UNSUPPORTED_MCP_VERSION');
47
47
  connected=true;
48
48
  return reply(m.id,{protocolVersion:m.params.protocolVersion,
49
- capabilities:{tools:{}},serverInfo:{name:'chat-loop-local-tools',version:'0.4.5'}});
49
+ capabilities:{tools:{}},serverInfo:{name:'chat-loop-local-tools',version:'0.4.6'}});
50
50
  }
51
51
  // A modern MCP client probes discovery before falling back to legacy initialize.
52
52
  // We only implement the legacy handshake era, so report unsupported method.
package/lib/tunnel.mjs ADDED
@@ -0,0 +1,66 @@
1
+ // Bundled OpenAI tunnel-client: never resolve from PATH, Homebrew or Local Dev.
2
+ import {createHash} from 'node:crypto';
3
+ import {createRequire} from 'node:module';
4
+ import {spawn} from 'node:child_process';
5
+ import {readFileSync,statSync} from 'node:fs';
6
+ import {homedir} from 'node:os';
7
+ import {join,dirname} from 'node:path';
8
+ import {fileURLToPath} from 'node:url';
9
+ const require=createRequire(import.meta.url);
10
+
11
+ const supported=new Set(['darwin-x64','darwin-arm64']);
12
+ export function bundledTunnel(){
13
+ const platform=process.platform+'-'+process.arch;
14
+ if(!supported.has(platform))throw Object.assign(Error('Bundled tunnel-client is not available on '+platform),
15
+ {code:'UNSUPPORTED_PLATFORM'});
16
+ const platformPackage='@tomflow/chatloop-tunnel-'+platform;
17
+ let base;
18
+ try{base=join(dirname(require.resolve(platformPackage+'/package.json')),'bin');}
19
+ catch{
20
+ // Repo-only test fallback. No sibling workspace in a published npm consumer.
21
+ base=fileURLToPath(new URL('../../tunnel-client-'+platform+'/bin/',import.meta.url));
22
+ }
23
+ const info=JSON.parse(readFileSync(join(base,'provenance.json'),'utf8'));
24
+ const binary=join(base,'tunnel-client');
25
+ const st=statSync(binary);
26
+ if(!st.isFile()||!(st.mode&0o111))
27
+ throw Object.assign(Error('Bundled executable missing'),{code:'INVALID_BUNDLE'});
28
+ const checksum=createHash('sha256').update(readFileSync(binary)).digest('hex');
29
+ if(checksum!==info.tunnelClientSha256)
30
+ throw Object.assign(Error('Bundled executable checksum mismatch'),{code:'INVALID_BUNDLE'});
31
+ return {binary,cloudflared:join(base,'cloudflared'),release:info.release,platform,checksum,base};
32
+ }
33
+ export function tunnelDoctor(){
34
+ try{
35
+ const b=bundledTunnel();
36
+ const cloudflared=createHash('sha256').update(readFileSync(b.cloudflared)).digest('hex');
37
+ const info=JSON.parse(readFileSync(join(b.base,'provenance.json'),'utf8'));
38
+ if(cloudflared!==info.cloudflaredSha256)
39
+ throw Object.assign(Error('Bundled cloudflared checksum mismatch'),{code:'INVALID_BUNDLE'});
40
+ return {status:'BUNDLED_READY',release:b.release,platform:b.platform,
41
+ executable:b.binary,cloudflared:b.cloudflared,verified:true,connection:'UNVERIFIED'};
42
+ }catch(e){return {status:'BLOCKED',code:e.code||'INVALID_BUNDLE',reason:e.message};}
43
+ }
44
+ // Explicitly scoped to this product. Do not touch a sibling Local Dev profile.
45
+ export async function runTunnel(args){
46
+ const b=bundledTunnel();
47
+ if(args[0]==='run'){
48
+ const hasProfileDir=args.some(x=>x==='--profile-dir'||x.startsWith('--profile-dir='));
49
+ const hasProfile=args.some(x=>x==='--profile'||x.startsWith('--profile='));
50
+ if(!hasProfileDir)args=[...args,'--profile-dir',join(homedir(),'.chat-loop','tunnel','profiles')];
51
+ if(!hasProfile)args=[...args,'--profile','chat-loop'];
52
+ }
53
+ await new Promise((resolve,reject)=>{
54
+ const child=spawn(b.binary,args,{stdio:'inherit',env:process.env,windowsHide:true,
55
+ cwd:b.base}); // Only this package's verified binary. Never use the system PATH.
56
+ child.once('error',reject);
57
+ const forward=signal=>child.kill(signal);
58
+ process.on('SIGINT',forward);process.on('SIGTERM',forward);
59
+ child.once('exit',(code,signal)=>{
60
+ process.off('SIGINT',forward);process.off('SIGTERM',forward);
61
+ if(signal){process.exitCode=1;return resolve();}
62
+ process.exitCode=code===null?1:code;
63
+ resolve();
64
+ });
65
+ });
66
+ }
package/package.json CHANGED
@@ -3,9 +3,9 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.4.5",
6
+ "version": "0.4.6",
7
7
  "type": "module",
8
- "description": "Chat Loop local tools: MCP gateway and unified tool lifecycle CLI",
8
+ "description": "Chat Loop local tools: MCP gateway, isolated bundled tunnel-client and unified CLI",
9
9
  "bin": {
10
10
  "chatloop-tools": "bin/cli.mjs",
11
11
  "chat-loop-local": "bin/cli.mjs"
@@ -25,5 +25,9 @@
25
25
  },
26
26
  "engines": {
27
27
  "node": ">=20"
28
+ },
29
+ "optionalDependencies": {
30
+ "@tomflow/chatloop-tunnel-darwin-x64": "0.0.19",
31
+ "@tomflow/chatloop-tunnel-darwin-arm64": "0.0.19"
28
32
  }
29
33
  }