@skillfm/local 2.3.0 → 2.5.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.
Files changed (100) hide show
  1. package/dist/doctor.js.map +1 -1
  2. package/dist/guard/cli.d.ts.map +1 -1
  3. package/dist/guard/cli.js.map +1 -1
  4. package/dist/harness/kernels/deny-pipeline.js +1 -1
  5. package/dist/harness/kernels/deny-pipeline.js.map +1 -1
  6. package/dist/harness/writers.d.ts.map +1 -1
  7. package/dist/harness/writers.js +2 -0
  8. package/dist/harness/writers.js.map +1 -1
  9. package/dist/index.js +29 -3
  10. package/dist/index.js.map +1 -1
  11. package/dist/mcp-stdio/api-client.d.ts.map +1 -1
  12. package/dist/mcp-stdio/api-client.js +3 -0
  13. package/dist/mcp-stdio/api-client.js.map +1 -1
  14. package/dist/mcp-stdio/render-flow.d.ts.map +1 -1
  15. package/dist/mcp-stdio/render-flow.js.map +1 -1
  16. package/dist/mcp-stdio/request-context.d.ts.map +1 -1
  17. package/dist/mcp-stdio/request-context.js +1 -0
  18. package/dist/mcp-stdio/request-context.js.map +1 -1
  19. package/dist/mcp-stdio/server.d.ts.map +1 -1
  20. package/dist/mcp-stdio/server.js +75 -3
  21. package/dist/mcp-stdio/server.js.map +1 -1
  22. package/dist/mcp-stdio/sse-progress-client.js.map +1 -1
  23. package/dist/skill-installer/bundle-fetcher.d.ts +40 -0
  24. package/dist/skill-installer/bundle-fetcher.d.ts.map +1 -0
  25. package/dist/skill-installer/bundle-fetcher.js +133 -0
  26. package/dist/skill-installer/bundle-fetcher.js.map +1 -0
  27. package/dist/skill-installer/errors.d.ts +12 -0
  28. package/dist/skill-installer/errors.d.ts.map +1 -0
  29. package/dist/skill-installer/errors.js +42 -0
  30. package/dist/skill-installer/errors.js.map +1 -0
  31. package/dist/skill-installer/index.d.ts +20 -0
  32. package/dist/skill-installer/index.d.ts.map +1 -0
  33. package/dist/skill-installer/index.js +193 -0
  34. package/dist/skill-installer/index.js.map +1 -0
  35. package/dist/skill-installer/lockfile.d.ts +8 -0
  36. package/dist/skill-installer/lockfile.d.ts.map +1 -0
  37. package/dist/skill-installer/lockfile.js +52 -0
  38. package/dist/skill-installer/lockfile.js.map +1 -0
  39. package/dist/skill-installer/npm-installer.d.ts +16 -0
  40. package/dist/skill-installer/npm-installer.d.ts.map +1 -0
  41. package/dist/skill-installer/npm-installer.js +83 -0
  42. package/dist/skill-installer/npm-installer.js.map +1 -0
  43. package/dist/skill-installer/paths.d.ts +4 -0
  44. package/dist/skill-installer/paths.d.ts.map +1 -0
  45. package/dist/skill-installer/paths.js +16 -0
  46. package/dist/skill-installer/paths.js.map +1 -0
  47. package/dist/skill-installer/tar-extractor.d.ts +15 -0
  48. package/dist/skill-installer/tar-extractor.d.ts.map +1 -0
  49. package/dist/skill-installer/tar-extractor.js +56 -0
  50. package/dist/skill-installer/tar-extractor.js.map +1 -0
  51. package/dist/skill-md/template.js +2 -2
  52. package/dist/skill-runner/cli.d.ts +4 -0
  53. package/dist/skill-runner/cli.d.ts.map +1 -0
  54. package/dist/skill-runner/cli.js +81 -0
  55. package/dist/skill-runner/cli.js.map +1 -0
  56. package/dist/skill-runner/discovery.d.ts +3 -0
  57. package/dist/skill-runner/discovery.d.ts.map +1 -0
  58. package/dist/skill-runner/discovery.js +108 -0
  59. package/dist/skill-runner/discovery.js.map +1 -0
  60. package/dist/skill-runner/index.d.ts +9 -0
  61. package/dist/skill-runner/index.d.ts.map +1 -0
  62. package/dist/skill-runner/index.js +100 -0
  63. package/dist/skill-runner/index.js.map +1 -0
  64. package/dist/skill-runner/registry.d.ts +11 -0
  65. package/dist/skill-runner/registry.d.ts.map +1 -0
  66. package/dist/skill-runner/registry.js +79 -0
  67. package/dist/skill-runner/registry.js.map +1 -0
  68. package/dist/skill-runner/spawner.d.ts +14 -0
  69. package/dist/skill-runner/spawner.d.ts.map +1 -0
  70. package/dist/skill-runner/spawner.js +85 -0
  71. package/dist/skill-runner/spawner.js.map +1 -0
  72. package/dist/skill-runner/types.d.ts +62 -0
  73. package/dist/skill-runner/types.d.ts.map +1 -0
  74. package/dist/skill-runner/types.js +6 -0
  75. package/dist/skill-runner/types.js.map +1 -0
  76. package/dist/skill-tunnel/cli.d.ts +5 -0
  77. package/dist/skill-tunnel/cli.d.ts.map +1 -0
  78. package/dist/skill-tunnel/cli.js +205 -0
  79. package/dist/skill-tunnel/cli.js.map +1 -0
  80. package/dist/skill-tunnel/client.d.ts +56 -0
  81. package/dist/skill-tunnel/client.d.ts.map +1 -0
  82. package/dist/skill-tunnel/client.js +260 -0
  83. package/dist/skill-tunnel/client.js.map +1 -0
  84. package/dist/skill-tunnel/handshake.d.ts +35 -0
  85. package/dist/skill-tunnel/handshake.d.ts.map +1 -0
  86. package/dist/skill-tunnel/handshake.js +61 -0
  87. package/dist/skill-tunnel/handshake.js.map +1 -0
  88. package/dist/skill-tunnel/heartbeat.d.ts +34 -0
  89. package/dist/skill-tunnel/heartbeat.d.ts.map +1 -0
  90. package/dist/skill-tunnel/heartbeat.js +86 -0
  91. package/dist/skill-tunnel/heartbeat.js.map +1 -0
  92. package/dist/skill-tunnel/local-bridge.d.ts +30 -0
  93. package/dist/skill-tunnel/local-bridge.d.ts.map +1 -0
  94. package/dist/skill-tunnel/local-bridge.js +224 -0
  95. package/dist/skill-tunnel/local-bridge.js.map +1 -0
  96. package/dist/skill-tunnel/reconnect.d.ts +21 -0
  97. package/dist/skill-tunnel/reconnect.d.ts.map +1 -0
  98. package/dist/skill-tunnel/reconnect.js +72 -0
  99. package/dist/skill-tunnel/reconnect.js.map +1 -0
  100. package/package.json +5 -2
@@ -0,0 +1,205 @@
1
+ // M2 SkillTunnelClient — CLI subcommand
2
+ //
3
+ // `skillfm-local tunnel start|stop|status`
4
+ // 简易实现: start 在当前进程起 tunnel(不 daemon 化),stop/status 通过 PID 文件。
5
+ // daemon 化(launchd / nohup)留给上层部署脚本,这里只提供基础设施。
6
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, rmSync, } from 'node:fs';
7
+ import { join, dirname } from 'node:path';
8
+ import { homedir } from 'node:os';
9
+ import { readRegistry } from '../skill-runner/registry.js';
10
+ import { SkillTunnelClient } from './client.js';
11
+ const PID_FILE = join(homedir(), '.skillfm', 'tunnel.pid');
12
+ const DEFAULT_BRAIN_URL = process.env.SKILLFM_BRAIN_TUNNEL_URL ?? 'wss://api.skillfm.ai/v1/skill-tunnel';
13
+ function ensureDir() {
14
+ const dir = dirname(PID_FILE);
15
+ if (!existsSync(dir))
16
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
17
+ }
18
+ function parseFlags(args) {
19
+ const flags = {};
20
+ for (let i = 0; i < args.length; i += 1) {
21
+ const t = args[i];
22
+ if (!t.startsWith('--'))
23
+ continue;
24
+ const eq = t.indexOf('=');
25
+ if (eq > 0) {
26
+ flags[t.slice(2, eq)] = t.slice(eq + 1);
27
+ }
28
+ else {
29
+ const next = args[i + 1];
30
+ if (next !== undefined && !next.startsWith('--')) {
31
+ flags[t.slice(2)] = next;
32
+ i += 1;
33
+ }
34
+ else {
35
+ flags[t.slice(2)] = true;
36
+ }
37
+ }
38
+ }
39
+ return flags;
40
+ }
41
+ function readConfig() {
42
+ const path = join(homedir(), '.skillfm', 'config.json');
43
+ if (!existsSync(path))
44
+ return {};
45
+ try {
46
+ return JSON.parse(readFileSync(path, 'utf-8'));
47
+ }
48
+ catch {
49
+ return {};
50
+ }
51
+ }
52
+ function readPackageVersion() {
53
+ // 只读顶层 package.json 里版本;build 后 dist/ 同级已经没有 package.json,fallback 环境变量
54
+ return process.env.SKILLFM_LOCAL_VERSION ?? '0.0.0';
55
+ }
56
+ function isProcessAlive(pid) {
57
+ try {
58
+ process.kill(pid, 0);
59
+ return true;
60
+ }
61
+ catch {
62
+ return false;
63
+ }
64
+ }
65
+ export async function cmdTunnelStart() {
66
+ const flags = parseFlags(process.argv.slice(3));
67
+ const brainUrl = typeof flags['brain-url'] === 'string' ? flags['brain-url'] : DEFAULT_BRAIN_URL;
68
+ // 检查是否已在跑
69
+ if (existsSync(PID_FILE)) {
70
+ const pid = Number.parseInt(readFileSync(PID_FILE, 'utf-8'), 10);
71
+ if (Number.isFinite(pid) && isProcessAlive(pid)) {
72
+ console.log(JSON.stringify({
73
+ ok: false,
74
+ error: 'ALREADY_RUNNING',
75
+ message: `tunnel 已在跑 (pid=${pid})`,
76
+ }));
77
+ process.exit(1);
78
+ return;
79
+ }
80
+ // PID 文件残留,清理
81
+ try {
82
+ rmSync(PID_FILE);
83
+ }
84
+ catch {
85
+ // ignore
86
+ }
87
+ }
88
+ const cfg = readConfig();
89
+ const agentToken = process.env.SKILLFM_AGENT_TOKEN ?? cfg.agent_token ?? '';
90
+ const authSecret = process.env.SKILLFM_SKILL_AUTH_SECRET ?? cfg.auth_secret ?? '';
91
+ if (!agentToken || !authSecret) {
92
+ console.log(JSON.stringify({
93
+ ok: false,
94
+ error: 'MISSING_CREDENTIALS',
95
+ message: 'agent_token / auth_secret 缺失. 请设 env SKILLFM_AGENT_TOKEN + SKILLFM_SKILL_AUTH_SECRET,或写入 ~/.skillfm/config.json',
96
+ }));
97
+ process.exit(1);
98
+ return;
99
+ }
100
+ const registry = readRegistry();
101
+ const localSkills = Object.values(registry.skills);
102
+ ensureDir();
103
+ writeFileSync(PID_FILE, String(process.pid), { mode: 0o600 });
104
+ const client = new SkillTunnelClient({
105
+ brainUrl,
106
+ agentToken,
107
+ authSecret,
108
+ localSkills,
109
+ skillfmLocalVersion: readPackageVersion(),
110
+ onStatusChange: (s) => {
111
+ console.error(`[tunnel] status=${s}`);
112
+ },
113
+ });
114
+ const cleanup = async () => {
115
+ try {
116
+ await client.stop();
117
+ }
118
+ finally {
119
+ try {
120
+ rmSync(PID_FILE);
121
+ }
122
+ catch {
123
+ // ignore
124
+ }
125
+ }
126
+ };
127
+ process.on('SIGINT', () => {
128
+ void cleanup().then(() => process.exit(0));
129
+ });
130
+ process.on('SIGTERM', () => {
131
+ void cleanup().then(() => process.exit(0));
132
+ });
133
+ console.log(JSON.stringify({
134
+ ok: true,
135
+ message: `tunnel 启动 pid=${process.pid} brain=${brainUrl} skills=${localSkills.length}`,
136
+ pid: process.pid,
137
+ }));
138
+ await client.start();
139
+ // 保持进程存活
140
+ await new Promise(() => { });
141
+ }
142
+ export function cmdTunnelStop() {
143
+ if (!existsSync(PID_FILE)) {
144
+ console.log(JSON.stringify({ ok: false, error: 'NOT_RUNNING' }));
145
+ process.exit(1);
146
+ return;
147
+ }
148
+ const pid = Number.parseInt(readFileSync(PID_FILE, 'utf-8'), 10);
149
+ if (!Number.isFinite(pid)) {
150
+ try {
151
+ rmSync(PID_FILE);
152
+ }
153
+ catch {
154
+ // ignore
155
+ }
156
+ console.log(JSON.stringify({ ok: false, error: 'BAD_PID_FILE' }));
157
+ process.exit(1);
158
+ return;
159
+ }
160
+ try {
161
+ process.kill(pid, 'SIGTERM');
162
+ console.log(JSON.stringify({ ok: true, pid, message: 'SIGTERM sent' }));
163
+ }
164
+ catch (err) {
165
+ console.log(JSON.stringify({
166
+ ok: false,
167
+ error: 'KILL_FAILED',
168
+ message: err.message,
169
+ }));
170
+ process.exit(1);
171
+ }
172
+ }
173
+ export function cmdTunnelStatus() {
174
+ if (!existsSync(PID_FILE)) {
175
+ console.log(JSON.stringify({ ok: true, running: false }));
176
+ return;
177
+ }
178
+ const pid = Number.parseInt(readFileSync(PID_FILE, 'utf-8'), 10);
179
+ const alive = Number.isFinite(pid) && isProcessAlive(pid);
180
+ console.log(JSON.stringify({ ok: true, running: alive, pid }));
181
+ }
182
+ export async function cmdTunnel() {
183
+ const sub = process.argv[3];
184
+ switch (sub) {
185
+ case 'start':
186
+ // shift argv so parseFlags 看到 `start` 之后的 flags
187
+ process.argv.splice(3, 1);
188
+ await cmdTunnelStart();
189
+ break;
190
+ case 'stop':
191
+ cmdTunnelStop();
192
+ break;
193
+ case 'status':
194
+ cmdTunnelStatus();
195
+ break;
196
+ default:
197
+ console.error(JSON.stringify({
198
+ ok: false,
199
+ error: 'UNKNOWN_SUBCOMMAND',
200
+ message: 'Usage: skillfm-local tunnel <start|stop|status> [--brain-url=<url>]',
201
+ }));
202
+ process.exit(1);
203
+ }
204
+ }
205
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/skill-tunnel/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ import { WebSocket } from 'ws';
2
+ import { TUNNEL_ERROR_CODES } from '@skillfm/contracts/skill-tunnel';
3
+ import type { HeartbeatTunnelResult, JsonRpcSuccessResponse, JsonRpcErrorResponse, SkillUpdateTunnelParams } from '@skillfm/contracts/skill-tunnel';
4
+ import type { LocalSkillRegistryEntry } from '../skill-runner/types.js';
5
+ export type TunnelStatus = 'connecting' | 'connected' | 'disconnected';
6
+ export interface SkillTunnelClientOptions {
7
+ /** e.g. wss://api.skillfm.ai/v1/skill-tunnel */
8
+ brainUrl: string;
9
+ agentToken: string;
10
+ /** 来自 M1 readRegistry() 的 entries 列表 */
11
+ localSkills: LocalSkillRegistryEntry[];
12
+ skillfmLocalVersion: string;
13
+ /** SKILLFM_SKILL_AUTH_SECRET — HMAC 密钥(handshake + JSON-RPC meta + 本地 skill 调用都用这把)*/
14
+ authSecret: string;
15
+ onStatusChange?: (status: TunnelStatus) => void;
16
+ /** 可选: 注入 fetch 给 LocalBridge(测试用)*/
17
+ fetch?: typeof fetch;
18
+ /** 可选: 注入 WebSocket 构造器(测试用)*/
19
+ webSocketCtor?: typeof WebSocket;
20
+ }
21
+ export declare class SkillTunnelClient {
22
+ private readonly opts;
23
+ private readonly bridge;
24
+ private readonly reconnect;
25
+ private readonly heartbeat;
26
+ private ws;
27
+ private status;
28
+ private lastConnectedAt;
29
+ private stopped;
30
+ private readonly pending;
31
+ constructor(opts: SkillTunnelClientOptions);
32
+ start(): Promise<void>;
33
+ stop(): Promise<void>;
34
+ status$(): {
35
+ connected: boolean;
36
+ lastConnectedAt: number | null;
37
+ reconnectAttempts: number;
38
+ };
39
+ /** 主动上报 skill lifecycle 事件 */
40
+ sendSkillUpdate(params: SkillUpdateTunnelParams): Promise<void>;
41
+ private connectOnce;
42
+ private onOpen;
43
+ private onError;
44
+ private onClose;
45
+ private onMessage;
46
+ private handleServerRequest;
47
+ private handleResponse;
48
+ private sendFrame;
49
+ private sendClientRequest;
50
+ private buildHeartbeatStatus;
51
+ private handleHeartbeatTimeout;
52
+ private setStatus;
53
+ }
54
+ export type { JsonRpcSuccessResponse, JsonRpcErrorResponse, HeartbeatTunnelResult };
55
+ export { TUNNEL_ERROR_CODES };
56
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/skill-tunnel/client.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,SAAS,EAAgB,MAAM,IAAI,CAAC;AAC7C,OAAO,EAGL,kBAAkB,EACnB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAEV,qBAAqB,EAIrB,sBAAsB,EACtB,oBAAoB,EAEpB,uBAAuB,EACxB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAMxE,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,WAAW,GAAG,cAAc,CAAC;AAEvE,MAAM,WAAW,wBAAwB;IACvC,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,WAAW,EAAE,uBAAuB,EAAE,CAAC;IACvC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,sFAAsF;IACtF,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;IAChD,qCAAqC;IACrC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,+BAA+B;IAC/B,aAAa,CAAC,EAAE,OAAO,SAAS,CAAC;CAClC;AAQD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA2B;IAChD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAC/C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgB;IAE1C,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,MAAM,CAAgC;IAC9C,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiC;gBAE7C,IAAI,EAAE,wBAAwB;IAkBpC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB3B,OAAO,IAAI;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE;IAQ5F,8BAA8B;IACxB,eAAe,CAAC,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;YAQvD,WAAW;IAyBzB,OAAO,CAAC,MAAM;IAOd,OAAO,CAAC,OAAO;IAKf,OAAO,CAAC,OAAO;IAsBf,OAAO,CAAC,SAAS;YAqBH,mBAAmB;IAKjC,OAAO,CAAC,cAAc;IAuBtB,OAAO,CAAC,SAAS;YASH,iBAAiB;IAsC/B,OAAO,CAAC,oBAAoB;IAc5B,OAAO,CAAC,sBAAsB;IAY9B,OAAO,CAAC,SAAS;CAKlB;AAGD,YAAY,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,CAAC;AACpF,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
@@ -0,0 +1,260 @@
1
+ // M2 SkillTunnelClient — WebSocket client + JSON-RPC dispatch
2
+ //
3
+ // 职责:
4
+ // - 连 brain (wss://api.skillfm.ai/v1/skill-tunnel) 带 TunnelHandshakeHeaders
5
+ // - 收 brain 发来的 skill.* JSON-RPC request → dispatch 到 LocalBridge → HTTP 回转 127.0.0.1
6
+ // - 主动发 tunnel.heartbeat / tunnel.skill_update(pending map 等 response)
7
+ // - 断线按 close code 决定是否重连;非致命走 ReconnectScheduler
8
+ //
9
+ // BYOK 纪律:
10
+ // - 全程 cookie / playwright profile 留本节点,client 只做"brain→本地 loopback"桥接
11
+ // - 凭证仅 agent_token / authSecret,存 ~/.skillfm/config.json(调用方 CLI 负责读)
12
+ //
13
+ // 反向契约:
14
+ // 输入: LocalSkillRegistryEntry[] 来自 M1 readRegistry()
15
+ // 输出: 对 brain 返回 JsonRpcSuccessResponse | JsonRpcErrorResponse
16
+ // 上游缺数据: 若 localSkills 为空 → 仍 connect,brain 侧 handshake 校验时决定 accept/reject(我们不假设)
17
+ // 下游假设我: handshake 头齐 9 个 + signature 正确
18
+ import { randomUUID } from 'node:crypto';
19
+ import { WebSocket } from 'ws';
20
+ import { TUNNEL_CONSTANTS, TUNNEL_SUBPROTOCOL, TUNNEL_ERROR_CODES, } from '@skillfm/contracts/skill-tunnel';
21
+ import { buildHandshakeHeaders, computeRpcMetaHmac } from './handshake.js';
22
+ import { HeartbeatLoop } from './heartbeat.js';
23
+ import { LocalBridge } from './local-bridge.js';
24
+ import { ReconnectScheduler, isFatalCloseCode } from './reconnect.js';
25
+ export class SkillTunnelClient {
26
+ opts;
27
+ bridge;
28
+ reconnect;
29
+ heartbeat;
30
+ ws = null;
31
+ status = 'disconnected';
32
+ lastConnectedAt = null;
33
+ stopped = false;
34
+ pending = new Map();
35
+ constructor(opts) {
36
+ this.opts = opts;
37
+ const endpointMap = new Map(opts.localSkills.map((s) => [s.slug, s.endpoint]));
38
+ this.bridge = new LocalBridge({
39
+ resolveEndpoint: (slug) => endpointMap.get(slug) ?? null,
40
+ skillAuthSecret: opts.authSecret,
41
+ fetch: opts.fetch,
42
+ });
43
+ this.reconnect = new ReconnectScheduler();
44
+ this.heartbeat = new HeartbeatLoop({
45
+ buildStatusSnapshot: () => this.buildHeartbeatStatus(),
46
+ sendHeartbeat: async (params) => {
47
+ await this.sendClientRequest('tunnel.heartbeat', params);
48
+ },
49
+ onTimeout: () => this.handleHeartbeatTimeout(),
50
+ });
51
+ }
52
+ async start() {
53
+ this.stopped = false;
54
+ await this.connectOnce();
55
+ }
56
+ async stop() {
57
+ this.stopped = true;
58
+ this.reconnect.stop();
59
+ this.heartbeat.stop();
60
+ for (const [, p] of this.pending) {
61
+ clearTimeout(p.timer);
62
+ p.reject(new Error('tunnel stopped'));
63
+ }
64
+ this.pending.clear();
65
+ if (this.ws && this.ws.readyState !== WebSocket.CLOSED) {
66
+ try {
67
+ this.ws.close(1000, 'graceful');
68
+ }
69
+ catch {
70
+ // ignore
71
+ }
72
+ }
73
+ this.setStatus('disconnected');
74
+ }
75
+ status$() {
76
+ return {
77
+ connected: this.status === 'connected',
78
+ lastConnectedAt: this.lastConnectedAt,
79
+ reconnectAttempts: this.reconnect.attempts,
80
+ };
81
+ }
82
+ /** 主动上报 skill lifecycle 事件 */
83
+ async sendSkillUpdate(params) {
84
+ await this.sendClientRequest('tunnel.skill_update', params);
85
+ }
86
+ // ==========================================================================
87
+ // 内部: 连接 / 事件
88
+ // ==========================================================================
89
+ async connectOnce() {
90
+ if (this.stopped)
91
+ return;
92
+ this.setStatus('connecting');
93
+ const { headers } = buildHandshakeHeaders({
94
+ agentToken: this.opts.agentToken,
95
+ authSecret: this.opts.authSecret,
96
+ localSkills: this.opts.localSkills,
97
+ skillfmLocalVersion: this.opts.skillfmLocalVersion,
98
+ });
99
+ const Ctor = this.opts.webSocketCtor ?? WebSocket;
100
+ // ws 的 headers 类型是 OutgoingHttpHeaders (index signature);TunnelHandshakeHeaders 是
101
+ // 强类型 9 字段对象,需要 cast 到宽类型才能传给 ws 构造器。
102
+ const ws = new Ctor(this.opts.brainUrl, [TUNNEL_SUBPROTOCOL], {
103
+ headers: headers,
104
+ });
105
+ this.ws = ws;
106
+ ws.on('open', () => this.onOpen());
107
+ ws.on('message', (raw) => this.onMessage(raw));
108
+ ws.on('error', (err) => this.onError(err));
109
+ ws.on('close', (code, reason) => this.onClose(code, reason));
110
+ }
111
+ onOpen() {
112
+ this.lastConnectedAt = Date.now();
113
+ this.reconnect.reset();
114
+ this.heartbeat.start();
115
+ this.setStatus('connected');
116
+ }
117
+ onError(err) {
118
+ // ws 的 'error' 事件在 close 前先触发,真正的重连判断放 close 里
119
+ console.error('[skill-tunnel] ws error:', err.message);
120
+ }
121
+ onClose(code, reason) {
122
+ this.heartbeat.stop();
123
+ for (const [, p] of this.pending) {
124
+ clearTimeout(p.timer);
125
+ p.reject(new Error(`ws closed (code=${code})`));
126
+ }
127
+ this.pending.clear();
128
+ this.setStatus('disconnected');
129
+ if (this.stopped)
130
+ return;
131
+ if (isFatalCloseCode(code)) {
132
+ console.error(`[skill-tunnel] fatal close code ${code}: ${reason.toString('utf-8')} — no reconnect`);
133
+ return;
134
+ }
135
+ this.reconnect.schedule(() => {
136
+ void this.connectOnce();
137
+ });
138
+ }
139
+ onMessage(raw) {
140
+ let frame;
141
+ try {
142
+ frame = JSON.parse(raw.toString());
143
+ }
144
+ catch {
145
+ console.error('[skill-tunnel] non-JSON frame dropped');
146
+ return;
147
+ }
148
+ // Response (brain 回我们主动发出的 heartbeat 等)
149
+ if ('result' in frame || 'error' in frame) {
150
+ this.handleResponse(frame);
151
+ return;
152
+ }
153
+ // Request (brain → agent, skill.*)
154
+ if ('method' in frame && 'id' in frame) {
155
+ void this.handleServerRequest(frame);
156
+ }
157
+ }
158
+ async handleServerRequest(req) {
159
+ const response = await this.bridge.dispatch(req.id, req.method, req.params);
160
+ this.sendFrame(response);
161
+ }
162
+ handleResponse(res) {
163
+ const p = this.pending.get(res.id);
164
+ if (!p) {
165
+ // heartbeat ack 可能没在 pending(fire-and-forget),更新 ack 时间就好
166
+ if ('result' in res) {
167
+ this.heartbeat.onAck();
168
+ }
169
+ return;
170
+ }
171
+ this.pending.delete(res.id);
172
+ clearTimeout(p.timer);
173
+ if ('error' in res) {
174
+ p.reject(new Error(`rpc error ${res.error.code}: ${res.error.message}`));
175
+ }
176
+ else {
177
+ this.heartbeat.onAck();
178
+ p.resolve(res.result);
179
+ }
180
+ }
181
+ // ==========================================================================
182
+ // 内部: 发送
183
+ // ==========================================================================
184
+ sendFrame(frame) {
185
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN)
186
+ return;
187
+ try {
188
+ this.ws.send(JSON.stringify(frame));
189
+ }
190
+ catch (err) {
191
+ console.error('[skill-tunnel] send failed:', err.message);
192
+ }
193
+ }
194
+ async sendClientRequest(method, params) {
195
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
196
+ throw new Error('tunnel not connected');
197
+ }
198
+ const id = randomUUID();
199
+ const req = {
200
+ jsonrpc: '2.0',
201
+ id,
202
+ method,
203
+ params,
204
+ meta: {
205
+ hmac: computeRpcMetaHmac(id, method, params, this.opts.authSecret),
206
+ },
207
+ };
208
+ return new Promise((resolve, reject) => {
209
+ const timer = setTimeout(() => {
210
+ this.pending.delete(id);
211
+ reject(new Error(`rpc timeout ${method}`));
212
+ }, TUNNEL_CONSTANTS.RPC_REQUEST_TIMEOUT_MS);
213
+ if (typeof timer.unref === 'function') {
214
+ timer.unref();
215
+ }
216
+ this.pending.set(id, {
217
+ resolve: (v) => resolve(v),
218
+ reject,
219
+ timer,
220
+ });
221
+ this.sendFrame(req);
222
+ });
223
+ }
224
+ // ==========================================================================
225
+ // Heartbeat 工具
226
+ // ==========================================================================
227
+ buildHeartbeatStatus() {
228
+ const out = {};
229
+ const now = Date.now();
230
+ for (const s of this.opts.localSkills) {
231
+ out[s.slug] = {
232
+ pid: s.server_pid,
233
+ rss_mb: 0, // M1 registry 暂不存内存; 后续可通过 process.memoryUsage 读
234
+ healthy: true,
235
+ last_check_at: s.last_health_at ?? now,
236
+ };
237
+ }
238
+ return out;
239
+ }
240
+ handleHeartbeatTimeout() {
241
+ console.error('[skill-tunnel] heartbeat timeout — reconnecting');
242
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
243
+ try {
244
+ this.ws.close(TUNNEL_CONSTANTS.HEARTBEAT_INTERVAL_MS, 'heartbeat timeout');
245
+ }
246
+ catch {
247
+ // ignore
248
+ }
249
+ }
250
+ // close 事件会触发 reconnect 调度
251
+ }
252
+ setStatus(s) {
253
+ if (this.status === s)
254
+ return;
255
+ this.status = s;
256
+ this.opts.onStatusChange?.(s);
257
+ }
258
+ }
259
+ export { TUNNEL_ERROR_CODES };
260
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/skill-tunnel/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import type { TunnelHandshakeHeaders } from '@skillfm/contracts/skill-tunnel';
2
+ import type { LocalSkillRegistryEntry } from '../skill-runner/types.js';
3
+ export interface BuildHandshakeInput {
4
+ agentToken: string;
5
+ /** SKILLFM_SKILL_AUTH_SECRET — HMAC 共享密钥 */
6
+ authSecret: string;
7
+ /** 本地已跑的 skill daemon 列表(来自 M1 readRegistry)*/
8
+ localSkills: LocalSkillRegistryEntry[];
9
+ /** @skillfm/local 运行时版本 */
10
+ skillfmLocalVersion: string;
11
+ }
12
+ export interface HandshakeArtifact {
13
+ headers: TunnelHandshakeHeaders;
14
+ /** 本次 nonce(保留供上层重放检测/调试;正式请求不传)*/
15
+ nonce: string;
16
+ }
17
+ /** 生成 16-byte nonce hex(契约 §5.1 16 byte hex = 32 字符)*/
18
+ export declare function generateNonce(): string;
19
+ export declare function computeAgentId(agentToken: string): string;
20
+ export declare function computeTunnelSignature(agentToken: string, nonce: string, authSecret: string): string;
21
+ /** 组装 `slug@version,slug@version` */
22
+ export declare function formatLocalSkills(skills: LocalSkillRegistryEntry[]): string;
23
+ /** 组装 `slug=http://127.0.0.1:port,slug=http://127.0.0.1:port` */
24
+ export declare function formatLocalSkillEndpoints(skills: LocalSkillRegistryEntry[]): string;
25
+ /**
26
+ * 构造一次握手所需的全部 9 个头。
27
+ * 每次 connect / reconnect 必须调用一次(nonce 随机)。
28
+ */
29
+ export declare function buildHandshakeHeaders(input: BuildHandshakeInput): HandshakeArtifact;
30
+ /**
31
+ * JSON-RPC meta.hmac 签名: HMAC-SHA256(id || method || JSON.stringify(params), secret)
32
+ * DELTA §5.4
33
+ */
34
+ export declare function computeRpcMetaHmac(id: string, method: string, params: unknown, authSecret: string): string;
35
+ //# sourceMappingURL=handshake.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handshake.d.ts","sourceRoot":"","sources":["../../src/skill-tunnel/handshake.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAC9E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAExE,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,+CAA+C;IAC/C,WAAW,EAAE,uBAAuB,EAAE,CAAC;IACvC,2BAA2B;IAC3B,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,sBAAsB,CAAC;IAChC,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;CACf;AAED,uDAAuD;AACvD,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,GACjB,MAAM,CAIR;AAED,qCAAqC;AACrC,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,uBAAuB,EAAE,GAAG,MAAM,CAE3E;AAED,iEAAiE;AACjE,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,uBAAuB,EAAE,GAChC,MAAM,CAER;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,mBAAmB,GACzB,iBAAiB,CAoBnB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,OAAO,EACf,UAAU,EAAE,MAAM,GACjB,MAAM,CAGR"}
@@ -0,0 +1,61 @@
1
+ // M2 SkillTunnelClient — handshake 头构造 & 签名
2
+ //
3
+ // 对应契约: shared/contracts/envelope-v2/skill-tunnel.ts `TunnelHandshakeHeaders`
4
+ // 9 个必填头:
5
+ // x-agent-token, x-agent-id, x-local-skills, x-local-skill-endpoints,
6
+ // x-tunnel-nonce, x-tunnel-signature, x-skillfm-local-version
7
+ //
8
+ // 签名算法 (DELTA §5.1):
9
+ // x-agent-id = SHA-256(agent_token) hex
10
+ // x-tunnel-signature = HMAC-SHA256(agent_token || nonce, SKILLFM_SKILL_AUTH_SECRET) hex
11
+ //
12
+ // 重放防护: 每次 connect (含 reconnect) 必须重新生成 nonce。
13
+ import { createHash, createHmac, randomBytes } from 'node:crypto';
14
+ /** 生成 16-byte nonce hex(契约 §5.1 16 byte hex = 32 字符)*/
15
+ export function generateNonce() {
16
+ return randomBytes(16).toString('hex');
17
+ }
18
+ export function computeAgentId(agentToken) {
19
+ return createHash('sha256').update(agentToken).digest('hex');
20
+ }
21
+ export function computeTunnelSignature(agentToken, nonce, authSecret) {
22
+ return createHmac('sha256', authSecret)
23
+ .update(agentToken + nonce)
24
+ .digest('hex');
25
+ }
26
+ /** 组装 `slug@version,slug@version` */
27
+ export function formatLocalSkills(skills) {
28
+ return skills.map((s) => `${s.slug}@${s.version}`).join(',');
29
+ }
30
+ /** 组装 `slug=http://127.0.0.1:port,slug=http://127.0.0.1:port` */
31
+ export function formatLocalSkillEndpoints(skills) {
32
+ return skills.map((s) => `${s.slug}=${s.endpoint}`).join(',');
33
+ }
34
+ /**
35
+ * 构造一次握手所需的全部 9 个头。
36
+ * 每次 connect / reconnect 必须调用一次(nonce 随机)。
37
+ */
38
+ export function buildHandshakeHeaders(input) {
39
+ const nonce = generateNonce();
40
+ const agentId = computeAgentId(input.agentToken);
41
+ const signature = computeTunnelSignature(input.agentToken, nonce, input.authSecret);
42
+ const headers = {
43
+ 'x-agent-token': input.agentToken,
44
+ 'x-agent-id': agentId,
45
+ 'x-local-skills': formatLocalSkills(input.localSkills),
46
+ 'x-local-skill-endpoints': formatLocalSkillEndpoints(input.localSkills),
47
+ 'x-tunnel-nonce': nonce,
48
+ 'x-tunnel-signature': signature,
49
+ 'x-skillfm-local-version': input.skillfmLocalVersion,
50
+ };
51
+ return { headers, nonce };
52
+ }
53
+ /**
54
+ * JSON-RPC meta.hmac 签名: HMAC-SHA256(id || method || JSON.stringify(params), secret)
55
+ * DELTA §5.4
56
+ */
57
+ export function computeRpcMetaHmac(id, method, params, authSecret) {
58
+ const payload = id + method + JSON.stringify(params ?? {});
59
+ return createHmac('sha256', authSecret).update(payload).digest('hex');
60
+ }
61
+ //# sourceMappingURL=handshake.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handshake.js","sourceRoot":"","sources":["../../src/skill-tunnel/handshake.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,EAAE;AACF,8EAA8E;AAC9E,UAAU;AACV,wEAAwE;AACxE,gEAAgE;AAChE,EAAE;AACF,qBAAqB;AACrB,0CAA0C;AAC1C,0FAA0F;AAC1F,EAAE;AACF,+CAA+C;AAE/C,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAoBlE,uDAAuD;AACvD,MAAM,UAAU,aAAa;IAC3B,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,UAAkB;IAC/C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,UAAkB,EAClB,KAAa,EACb,UAAkB;IAElB,OAAO,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC;SACpC,MAAM,CAAC,UAAU,GAAG,KAAK,CAAC;SAC1B,MAAM,CAAC,KAAK,CAAC,CAAC;AACnB,CAAC;AAED,qCAAqC;AACrC,MAAM,UAAU,iBAAiB,CAAC,MAAiC;IACjE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/D,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,yBAAyB,CACvC,MAAiC;IAEjC,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAChE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAA0B;IAE1B,MAAM,KAAK,GAAG,aAAa,EAAE,CAAC;IAC9B,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACjD,MAAM,SAAS,GAAG,sBAAsB,CACtC,KAAK,CAAC,UAAU,EAChB,KAAK,EACL,KAAK,CAAC,UAAU,CACjB,CAAC;IAEF,MAAM,OAAO,GAA2B;QACtC,eAAe,EAAE,KAAK,CAAC,UAAU;QACjC,YAAY,EAAE,OAAO;QACrB,gBAAgB,EAAE,iBAAiB,CAAC,KAAK,CAAC,WAAW,CAAC;QACtD,yBAAyB,EAAE,yBAAyB,CAAC,KAAK,CAAC,WAAW,CAAC;QACvE,gBAAgB,EAAE,KAAK;QACvB,oBAAoB,EAAE,SAAS;QAC/B,yBAAyB,EAAE,KAAK,CAAC,mBAAmB;KACrD,CAAC;IAEF,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAC5B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAChC,EAAU,EACV,MAAc,EACd,MAAe,EACf,UAAkB;IAElB,MAAM,OAAO,GAAG,EAAE,GAAG,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IAC3D,OAAO,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACxE,CAAC"}