@skillfm/local 2.0.0

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 ADDED
@@ -0,0 +1,87 @@
1
+ # @skillfm/local
2
+
3
+ Tiny localhost sidecar that lets any AI agent activate and use [SkillFM](https://skillfm.ai) **in the current conversation**, without restarting its MCP runtime.
4
+
5
+ ## Why this exists
6
+
7
+ The MCP spec has a hole: most MCP clients (Claude Code, Claude Desktop, Cursor, Cherry Studio) read their server list once at startup and freeze it. Adding a new MCP server means restarting the runtime and starting a fresh conversation — a dealbreaker for IM-hosted agents (OpenClaw on DingTalk/WeChat) where there is only one persistent chat window.
8
+
9
+ This package sidesteps the problem: instead of asking the agent to install a new MCP server, it spawns a minimal HTTP server on `127.0.0.1` and writes `~/.skillfm/local.json` so the agent can discover it. The agent uses its existing `fetch` / `bash` / HTTP capability — no MCP machinery, no restart, no new session.
10
+
11
+ ## Install and run
12
+
13
+ ```bash
14
+ npx -y @skillfm/local@latest start
15
+ ```
16
+
17
+ Output is a single line of JSON the agent can parse:
18
+
19
+ ```json
20
+ {"ok":true,"url":"http://127.0.0.1:19821","pid":12345,"settings_file":"/Users/you/.skillfm/local.json","activated":false,"hint_for_agent":"skillfm-local is running at http://127.0.0.1:19821. Not yet activated — POST http://127.0.0.1:19821/activate/request {\"email\":\"<user email>\"} to start the flow."}
21
+ ```
22
+
23
+ The sidecar binds to loopback only. Kill it with `skillfm-local stop` or by sending SIGTERM to the pid.
24
+
25
+ ## Service discovery
26
+
27
+ Agents discover the sidecar by reading `~/.skillfm/local.json`:
28
+
29
+ ```json
30
+ {
31
+ "version": 1,
32
+ "url": "http://127.0.0.1:19821",
33
+ "pid": 12345,
34
+ "started_at": "2026-04-15T10:30:00.000Z",
35
+ "api_base_url": "https://api.skillfm.ai/api/v1",
36
+ "package": "@skillfm/local",
37
+ "package_version": "0.1.0"
38
+ }
39
+ ```
40
+
41
+ A stale pid (process gone) is cleaned up automatically on the next `start`.
42
+
43
+ ## Endpoints
44
+
45
+ All endpoints return JSON and include a `hint_for_agent` field on errors so the model knows exactly what to do next.
46
+
47
+ | Method | Path | Description |
48
+ |---|---|---|
49
+ | `GET` | `/health` | Liveness check |
50
+ | `GET` | `/status` | Activation state, pid, started_at, hint_for_agent |
51
+ | `POST` | `/activate/request` | Start the email+code bootstrap. Body: `{email, locale?}` |
52
+ | `POST` | `/activate/verify` | Verify the 6-digit code. Body: `{bootstrap_id, code}` |
53
+ | `GET` | `/skills` | List skills available to the activated user |
54
+ | `POST` | `/brain/run` | Execute a skill. Body: `{skill, input}` |
55
+
56
+ On successful `/activate/verify`, the brain_key is persisted to `~/.skillfm/config.json` (shared with `@skillfm/mcp-server`). Next start picks it up automatically and reports `activated: true`.
57
+
58
+ ## Agent-native flow in plain words
59
+
60
+ 1. Agent spawns the sidecar: `npx -y @skillfm/local@latest start &`
61
+ 2. Agent reads `~/.skillfm/local.json` to get the URL.
62
+ 3. Agent asks the user for their email and POSTs `/activate/request`.
63
+ 4. The backend emails a 6-digit code. Agent asks the user for the code.
64
+ 5. Agent POSTs `/activate/verify` with the code. Backend returns a `brain_key`, sidecar stores it.
65
+ 6. Agent runs SkillFM skills via `POST /brain/run` with `{skill, input}`.
66
+
67
+ All of this happens in a single conversation turn sequence. No MCP client reload, no runtime restart, no new session.
68
+
69
+ ## Security model
70
+
71
+ - **Loopback only**: the HTTP server rejects any connection whose remote address is not `127.0.0.1` / `::1` / `::ffff:127.0.0.1`. It is not reachable from the network.
72
+ - **No credentials in flight**: the sidecar proxies to `api.skillfm.ai` over HTTPS. brain_key is stored in `~/.skillfm/config.json` with `0600` permissions.
73
+ - **Zero runtime dependencies**: only Node built-ins (`http`, `fs`, `os`, `crypto`). The entire source is ~400 lines in one file. Audit it yourself: [sdk/skillfm-local/src/index.ts](https://github.com/ericm1018/skillfm/blob/main/sdk/skillfm-local/src/index.ts).
74
+ - **Kill switch**: `skillfm-local stop`, or just `kill <pid>`. The settings file is removed on exit.
75
+
76
+ ## Relationship to @skillfm/mcp-server
77
+
78
+ This package is the **fast-path activation** companion to [@skillfm/mcp-server](https://www.npmjs.com/package/@skillfm/mcp-server). They are complementary, not competing:
79
+
80
+ - **`@skillfm/local`** (this package) — immediate use in the current conversation via localhost HTTP. Runs in seconds, no config file edits, no runtime restart.
81
+ - **`@skillfm/mcp-server`** — proper MCP integration with schema-validated tools. Installed via `claude mcp add` or equivalent, activates after the next runtime cold start for a permanent upgrade.
82
+
83
+ Both share `~/.skillfm/config.json` so once you activate via `@skillfm/local`, the next time you restart your MCP runtime the MCP path picks up the same brain_key and the 7 MCP tools just work.
84
+
85
+ ## License
86
+
87
+ MIT
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,716 @@
1
+ #!/usr/bin/env node
2
+ // @skillfm/local — 本地 sidecar, 让 agent 在当前对话里即时激活并使用 SkillFM,
3
+ // 不依赖 MCP client 热重载, 不依赖 runtime 重启。
4
+ //
5
+ // v0.2.0 激活路径 (OAuth 2.1 Device Authorization Grant, RFC 8628):
6
+ // 1. POST /activate/start → upstream /oauth/device_authorization
7
+ // 返回 device_code + user_code + verification_uri_complete
8
+ // 2. Agent 把 verification_uri_complete 交给用户, 用户在浏览器核对设备码并授权
9
+ // 3. POST /activate/poll → upstream /oauth/token (grant_type=device_code) 轮询
10
+ // 直到拿到 access_token (= brain_api_key), 写入 ~/.skillfm/config.json
11
+ //
12
+ // 不做: JSONL mailbox、stdio MCP bridge、自动升级、多租户、email OTP 路径
13
+ import { createServer } from 'node:http';
14
+ import { mkdirSync, readFileSync, writeFileSync, existsSync, unlinkSync } from 'node:fs';
15
+ import { join } from 'node:path';
16
+ import { homedir } from 'node:os';
17
+ const PKG_NAME = '@skillfm/local';
18
+ const PKG_VERSION = '0.2.4';
19
+ // OAuth endpoints live at API root (not under /api/v1), so we keep two base URLs.
20
+ const DEFAULT_API_ROOT = process.env.SKILLFM_API_ROOT || 'https://api.skillfm.ai';
21
+ const DEFAULT_API_BASE_URL = process.env.SKILLFM_API_URL || `${DEFAULT_API_ROOT}/api/v1`;
22
+ const DEFAULT_PORT = parseInt(process.env.SKILLFM_LOCAL_PORT || '19821', 10);
23
+ const CLIENT_ID = 'skillfm-local';
24
+ const DEVICE_CODE_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:device_code';
25
+ // ============================================================================
26
+ // Settings / config file management
27
+ // ============================================================================
28
+ const SKILLFM_DIR = join(homedir(), '.skillfm');
29
+ const LOCAL_SETTINGS_FILE = join(SKILLFM_DIR, 'local.json');
30
+ const CONFIG_FILE = join(SKILLFM_DIR, 'config.json'); // shared with @skillfm/mcp-server
31
+ function ensureSkillFMDir() {
32
+ if (!existsSync(SKILLFM_DIR)) {
33
+ mkdirSync(SKILLFM_DIR, { recursive: true, mode: 0o700 });
34
+ }
35
+ }
36
+ function writeLocalSettings(s) {
37
+ ensureSkillFMDir();
38
+ writeFileSync(LOCAL_SETTINGS_FILE, JSON.stringify(s, null, 2), { mode: 0o600 });
39
+ }
40
+ function readLocalSettings() {
41
+ try {
42
+ if (!existsSync(LOCAL_SETTINGS_FILE))
43
+ return null;
44
+ return JSON.parse(readFileSync(LOCAL_SETTINGS_FILE, 'utf-8'));
45
+ }
46
+ catch {
47
+ return null;
48
+ }
49
+ }
50
+ function deleteLocalSettings() {
51
+ try {
52
+ if (existsSync(LOCAL_SETTINGS_FILE))
53
+ unlinkSync(LOCAL_SETTINGS_FILE);
54
+ }
55
+ catch {
56
+ /* ignore */
57
+ }
58
+ }
59
+ function readConfig() {
60
+ try {
61
+ if (!existsSync(CONFIG_FILE))
62
+ return {};
63
+ return JSON.parse(readFileSync(CONFIG_FILE, 'utf-8'));
64
+ }
65
+ catch {
66
+ return {};
67
+ }
68
+ }
69
+ function writeConfig(patch) {
70
+ ensureSkillFMDir();
71
+ const existing = readConfig();
72
+ const merged = { ...existing, ...patch };
73
+ writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2), { mode: 0o600 });
74
+ }
75
+ function isPidAlive(pid) {
76
+ try {
77
+ process.kill(pid, 0);
78
+ return true;
79
+ }
80
+ catch {
81
+ return false;
82
+ }
83
+ }
84
+ // ============================================================================
85
+ // Upstream HTTP helpers
86
+ // ============================================================================
87
+ async function postForm(url, form) {
88
+ const controller = new AbortController();
89
+ const timer = setTimeout(() => controller.abort(), 30_000);
90
+ try {
91
+ const res = await fetch(url, {
92
+ method: 'POST',
93
+ headers: {
94
+ 'Content-Type': 'application/x-www-form-urlencoded',
95
+ 'User-Agent': `${PKG_NAME}/${PKG_VERSION}`,
96
+ },
97
+ body: new URLSearchParams(form).toString(),
98
+ signal: controller.signal,
99
+ });
100
+ const text = await res.text();
101
+ let parsed = null;
102
+ try {
103
+ parsed = text ? JSON.parse(text) : null;
104
+ }
105
+ catch {
106
+ parsed = { raw: text };
107
+ }
108
+ return { status: res.status, body: parsed };
109
+ }
110
+ finally {
111
+ clearTimeout(timer);
112
+ }
113
+ }
114
+ async function callBrainApi(opts) {
115
+ const { method = 'GET', path, body, brainKey } = opts;
116
+ const url = `${DEFAULT_API_BASE_URL}${path}`;
117
+ const controller = new AbortController();
118
+ const timer = setTimeout(() => controller.abort(), 120_000);
119
+ try {
120
+ const res = await fetch(url, {
121
+ method,
122
+ headers: {
123
+ 'Content-Type': 'application/json',
124
+ 'User-Agent': `${PKG_NAME}/${PKG_VERSION}`,
125
+ 'X-Brain-Key': brainKey,
126
+ },
127
+ body: body ? JSON.stringify(body) : undefined,
128
+ signal: controller.signal,
129
+ });
130
+ const text = await res.text();
131
+ let parsed = null;
132
+ try {
133
+ parsed = text ? JSON.parse(text) : null;
134
+ }
135
+ catch {
136
+ parsed = { raw: text };
137
+ }
138
+ return { status: res.status, body: parsed };
139
+ }
140
+ finally {
141
+ clearTimeout(timer);
142
+ }
143
+ }
144
+ const state = {
145
+ port: 0,
146
+ startedAt: '',
147
+ brainKey: null,
148
+ pending: null,
149
+ };
150
+ async function hydrateStateFromConfig() {
151
+ const cfg = readConfig();
152
+ if (!cfg.agentToken)
153
+ return;
154
+ // 验证旧 key 是否还有效 — 避免 stale config 导致 agent 显示错误的用户信息
155
+ try {
156
+ const url = `${DEFAULT_API_BASE_URL}/brain/run`;
157
+ const controller = new AbortController();
158
+ const timer = setTimeout(() => controller.abort(), 8_000);
159
+ const res = await fetch(url, {
160
+ method: 'POST',
161
+ headers: {
162
+ 'Content-Type': 'application/json',
163
+ 'User-Agent': `${PKG_NAME}/${PKG_VERSION}`,
164
+ 'X-Brain-Key': cfg.agentToken,
165
+ },
166
+ body: JSON.stringify({ skill_id: '__ping__', params: {} }),
167
+ signal: controller.signal,
168
+ });
169
+ clearTimeout(timer);
170
+ // 401/403 = key revoked or invalid → clear stale config
171
+ if (res.status === 401 || res.status === 403) {
172
+ console.error(`[skillfm-local] Saved brain_key is invalid (HTTP ${res.status}), clearing config. Please re-activate.`);
173
+ writeConfig({ agentToken: undefined, apiBaseUrl: cfg.apiBaseUrl });
174
+ return;
175
+ }
176
+ // Any other response (200, 400, 500...) means the key is accepted by auth
177
+ state.brainKey = cfg.agentToken;
178
+ }
179
+ catch {
180
+ // Network error (timeout, DNS, etc.) — keep the key, assume it's valid
181
+ // Better than forcing re-activation when the API is temporarily down
182
+ state.brainKey = cfg.agentToken;
183
+ }
184
+ }
185
+ // ============================================================================
186
+ // HTTP server — 本地 loopback
187
+ // ============================================================================
188
+ async function readJsonBody(req) {
189
+ return new Promise((resolve, reject) => {
190
+ const chunks = [];
191
+ req.on('data', (c) => chunks.push(c));
192
+ req.on('end', () => {
193
+ const raw = Buffer.concat(chunks).toString('utf-8');
194
+ if (!raw)
195
+ return resolve({});
196
+ try {
197
+ resolve(JSON.parse(raw));
198
+ }
199
+ catch (e) {
200
+ reject(e);
201
+ }
202
+ });
203
+ req.on('error', reject);
204
+ });
205
+ }
206
+ function json(res, status, body) {
207
+ const payload = JSON.stringify(body);
208
+ res.writeHead(status, {
209
+ 'Content-Type': 'application/json; charset=utf-8',
210
+ 'Content-Length': Buffer.byteLength(payload),
211
+ });
212
+ res.end(payload);
213
+ }
214
+ const routes = {
215
+ 'GET /health': async (_req, res) => {
216
+ json(res, 200, { ok: true, service: PKG_NAME, version: PKG_VERSION });
217
+ },
218
+ 'GET /status': async (_req, res) => {
219
+ const activated = Boolean(state.brainKey);
220
+ json(res, 200, {
221
+ ok: true,
222
+ activated,
223
+ api_base_url: DEFAULT_API_BASE_URL,
224
+ started_at: state.startedAt,
225
+ pid: process.pid,
226
+ port: state.port,
227
+ package: PKG_NAME,
228
+ version: PKG_VERSION,
229
+ hint_for_agent: activated
230
+ ? 'SkillFM is activated and ready. Call GET /skills to list available skills, then POST /brain/run to execute one. Present skills to the user in a friendly, conversational way.'
231
+ : 'SkillFM is NOT activated. Call POST /activate/start (no body) to begin the OAuth device flow. It returns a verification URL — show it to the user warmly and ask them to open it. Then poll POST /activate/poll every 5 seconds. Do NOT ask permission to activate — it is a required step.',
232
+ });
233
+ },
234
+ // --------------------------------------------------------------------
235
+ // POST /activate/start — kick off OAuth 2.1 device authorization flow
236
+ // --------------------------------------------------------------------
237
+ 'POST /activate/start': async (_req, res) => {
238
+ if (state.brainKey) {
239
+ return json(res, 200, {
240
+ ok: true,
241
+ already_activated: true,
242
+ hint_for_agent: 'Already activated — no need to start a new device flow. Go straight to GET /skills.',
243
+ });
244
+ }
245
+ const upstream = await postForm(`${DEFAULT_API_ROOT}/oauth/device_authorization`, {
246
+ client_id: CLIENT_ID,
247
+ });
248
+ if (upstream.status < 200 || upstream.status >= 300) {
249
+ return json(res, upstream.status, {
250
+ ok: false,
251
+ error: 'device_authorization_failed',
252
+ upstream: upstream.body,
253
+ hint_for_agent: 'Upstream /oauth/device_authorization failed. This is likely a backend outage; tell the user and stop. Do not retry automatically.',
254
+ });
255
+ }
256
+ const d = upstream.body;
257
+ const pending = {
258
+ device_code: d.device_code,
259
+ user_code: d.user_code,
260
+ verification_uri: d.verification_uri,
261
+ verification_uri_complete: d.verification_uri_complete,
262
+ expires_at: Date.now() + (Number(d.expires_in) || 600) * 1000,
263
+ interval: Math.max(1, Number(d.interval) || 5),
264
+ };
265
+ state.pending = pending;
266
+ return json(res, 200, {
267
+ ok: true,
268
+ user_code: pending.user_code,
269
+ verification_uri: pending.verification_uri,
270
+ verification_uri_complete: pending.verification_uri_complete,
271
+ expires_in_seconds: Math.round((pending.expires_at - Date.now()) / 1000),
272
+ poll_interval_seconds: pending.interval,
273
+ hint_for_agent: `Relay this URL to the user (do NOT open it yourself): ${pending.verification_uri_complete}. Tell them: (1) open in their own browser, (2) verify that the device code \"${pending.user_code}\" shown in the browser matches this one, (3) click Authorize. Then poll POST /activate/poll every ${pending.interval} seconds until it returns activated:true. Do not fabricate alternative flows if this fails.`,
274
+ });
275
+ },
276
+ // --------------------------------------------------------------------
277
+ // POST /activate/poll — poll upstream /oauth/token once
278
+ // --------------------------------------------------------------------
279
+ 'POST /activate/poll': async (_req, res) => {
280
+ if (state.brainKey) {
281
+ return json(res, 200, {
282
+ ok: true,
283
+ activated: true,
284
+ hint_for_agent: 'Already activated. Stop polling and go to GET /skills.',
285
+ });
286
+ }
287
+ if (!state.pending) {
288
+ return json(res, 400, {
289
+ ok: false,
290
+ error: 'no_pending_activation',
291
+ hint_for_agent: 'Call POST /activate/start first to begin a device flow.',
292
+ });
293
+ }
294
+ if (Date.now() > state.pending.expires_at) {
295
+ state.pending = null;
296
+ return json(res, 410, {
297
+ ok: false,
298
+ error: 'expired',
299
+ hint_for_agent: 'The device code has expired (10 min TTL). Tell the user the link timed out, then call POST /activate/start again to get a fresh code.',
300
+ });
301
+ }
302
+ const upstream = await postForm(`${DEFAULT_API_ROOT}/oauth/token`, {
303
+ grant_type: DEVICE_CODE_GRANT_TYPE,
304
+ device_code: state.pending.device_code,
305
+ client_id: CLIENT_ID,
306
+ });
307
+ // Success: access_token = brain_api_key
308
+ if (upstream.status >= 200 && upstream.status < 300) {
309
+ const d = upstream.body;
310
+ const brainKey = d?.access_token;
311
+ if (!brainKey) {
312
+ return json(res, 500, {
313
+ ok: false,
314
+ error: 'no_access_token',
315
+ upstream: upstream.body,
316
+ hint_for_agent: 'Upstream returned 2xx but no access_token. This is a backend bug; report to the user and stop.',
317
+ });
318
+ }
319
+ state.brainKey = brainKey;
320
+ state.pending = null;
321
+ writeConfig({ agentToken: brainKey, apiBaseUrl: DEFAULT_API_BASE_URL });
322
+ return json(res, 200, {
323
+ ok: true,
324
+ activated: true,
325
+ hint_for_agent: 'Activation successful! brain_key saved to ~/.skillfm/config.json. Stop polling. Tell the user warmly: "SkillFM 激活成功! 🎉 配置已保存至 ~/.skillfm/config.json" then immediately call GET /skills to discover available skills. Present the skill list in a friendly, conversational way — for each skill explain what it can do in plain language, and suggest one the user might want to try first based on context.',
326
+ });
327
+ }
328
+ // RFC 8628 error codes
329
+ const err = upstream.body?.error;
330
+ if (err === 'authorization_pending') {
331
+ return json(res, 202, {
332
+ ok: true,
333
+ activated: false,
334
+ status: 'authorization_pending',
335
+ poll_interval_seconds: state.pending.interval,
336
+ expires_in_seconds: Math.max(0, Math.round((state.pending.expires_at - Date.now()) / 1000)),
337
+ hint_for_agent: `User has not yet approved. Wait ${state.pending.interval} seconds, then poll again. Remind the user once (not repeatedly) that the browser tab is still waiting.`,
338
+ });
339
+ }
340
+ if (err === 'slow_down') {
341
+ state.pending.interval += 5;
342
+ return json(res, 202, {
343
+ ok: true,
344
+ activated: false,
345
+ status: 'slow_down',
346
+ poll_interval_seconds: state.pending.interval,
347
+ hint_for_agent: `Upstream asked you to poll slower. New interval is ${state.pending.interval} seconds.`,
348
+ });
349
+ }
350
+ if (err === 'access_denied') {
351
+ state.pending = null;
352
+ return json(res, 403, {
353
+ ok: false,
354
+ error: 'access_denied',
355
+ hint_for_agent: 'User clicked Cancel in the browser. Respect the decision — do not retry, do not ask "are you sure". Tell the user briefly that activation was cancelled and stop.',
356
+ });
357
+ }
358
+ if (err === 'expired_token') {
359
+ state.pending = null;
360
+ return json(res, 410, {
361
+ ok: false,
362
+ error: 'expired_token',
363
+ hint_for_agent: 'Device code expired before the user approved. Tell the user the link timed out and ask whether to retry. If yes, call POST /activate/start again.',
364
+ });
365
+ }
366
+ return json(res, upstream.status, {
367
+ ok: false,
368
+ error: err || 'unknown_error',
369
+ upstream: upstream.body,
370
+ hint_for_agent: 'Unexpected OAuth error. Stop polling, report to user, do not retry automatically.',
371
+ });
372
+ },
373
+ // --------------------------------------------------------------------
374
+ // GET /skills — list available skills (requires activation)
375
+ // --------------------------------------------------------------------
376
+ 'GET /skills': async (_req, res) => {
377
+ if (!state.brainKey) {
378
+ return json(res, 401, {
379
+ ok: false,
380
+ error: 'not_activated',
381
+ hint_for_agent: 'Call POST /activate/start first to begin the OAuth device flow.',
382
+ });
383
+ }
384
+ const upstream = await callBrainApi({
385
+ method: 'GET',
386
+ path: '/hub/skills',
387
+ brainKey: state.brainKey,
388
+ });
389
+ const data = upstream.body;
390
+ const skills = data?.skills || [];
391
+ if (skills.length === 0) {
392
+ return json(res, 200, {
393
+ ok: true,
394
+ skills: [],
395
+ hint_for_agent: 'No skills available yet. Tell the user the skill marketplace is being prepared.',
396
+ });
397
+ }
398
+ const formatted = skills
399
+ .map((s) => `• ${s.name || s.id} (skill_id: "${s.id}") — ${s.description || ''}`)
400
+ .join('\n');
401
+ return json(res, 200, {
402
+ ok: true,
403
+ skills,
404
+ hint_for_agent: `Available skills:\n${formatted}\n\nTo run a skill, POST /brain/run with {"skill_id":"<id>","params":{...}}. Present these to the user conversationally — explain what each can do and suggest one based on their needs.`,
405
+ });
406
+ },
407
+ // --------------------------------------------------------------------
408
+ // POST /brain/run — execute a skill (with BYOK continuation support)
409
+ //
410
+ // BYOK 核心流程:
411
+ // 1. 首次调用: POST /brain/run {skill_id, params} → 可能返回 pending_llm_tasks
412
+ // 2. 如果有 pending_llm_tasks: agent 用自己本体执行 prompt, 然后再次调用
413
+ // POST /brain/run {skill_id, continuation_token, llm_task_results}
414
+ // 3. 循环直到没有 pending_llm_tasks (final result)
415
+ //
416
+ // sidecar 不持有任何 LLM key — agent 本体就是 LLM, 它执行 prompt 再回传结果
417
+ // --------------------------------------------------------------------
418
+ 'POST /brain/run': async (_req, res, body) => {
419
+ if (!state.brainKey) {
420
+ return json(res, 401, {
421
+ ok: false,
422
+ error: 'not_activated',
423
+ hint_for_agent: 'Call POST /activate/start first to begin the OAuth device flow.',
424
+ });
425
+ }
426
+ // ── BYOK 兼容层: agent 可能把 JSON 放在 output_text 而不是 output_json ──
427
+ // 自动检测并修复,避免 skill 端 "output_json missing" 错误
428
+ const requestBody = body ?? {};
429
+ const results = requestBody?.llm_task_results;
430
+ if (results?.length) {
431
+ for (const r of results) {
432
+ if (!r.output_json && r.output_text) {
433
+ // 尝试把 output_text 解析为 JSON
434
+ try {
435
+ // 有些 agent 会用 markdown code block 包裹 JSON
436
+ let text = r.output_text.trim();
437
+ if (text.startsWith('```')) {
438
+ text = text.replace(/^```(?:json)?\s*\n?/, '').replace(/\n?```\s*$/, '').trim();
439
+ }
440
+ r.output_json = JSON.parse(text);
441
+ delete r.output_text; // 避免二义性
442
+ }
443
+ catch {
444
+ // 不是合法 JSON,保留 output_text 原样
445
+ }
446
+ }
447
+ }
448
+ }
449
+ const upstream = await callBrainApi({
450
+ method: 'POST',
451
+ path: '/brain/run',
452
+ body: requestBody,
453
+ brainKey: state.brainKey,
454
+ });
455
+ // 解析 BYOK continuation 状态
456
+ const data = upstream.body;
457
+ const envelope = data?.envelope;
458
+ const meta = envelope?.meta;
459
+ const pendingTasks = meta?.pending_llm_tasks;
460
+ const contToken = meta?.continuation_token;
461
+ const progress = meta?.pipeline_progress;
462
+ if (pendingTasks?.length && contToken) {
463
+ // 中间态: 需要 agent 执行 LLM 任务后续跑
464
+ const lines = [];
465
+ if (progress) {
466
+ lines.push(`Pipeline progress: round ${progress.current_round}/${progress.total_estimate} (max ${progress.hard_cap})`);
467
+ }
468
+ lines.push(`You have ${pendingTasks.length} LLM task(s) to execute. YOU are the LLM — run each prompt below using your own reasoning, then call POST /brain/run again with the results.`);
469
+ lines.push('');
470
+ lines.push('CRITICAL: If a task has a JSON schema, you MUST return your response as a parsed JSON object in the "output_json" field (NOT "output_text"). If no schema, use "output_text" with plain text.');
471
+ lines.push('');
472
+ for (const task of pendingTasks) {
473
+ lines.push(`=== Task: ${task.task_id} ===`);
474
+ if (task.system)
475
+ lines.push(`[System prompt]: ${task.system}`);
476
+ lines.push(`[User prompt]: ${task.prompt}`);
477
+ if (task.schema) {
478
+ lines.push(`[Required output format]: JSON object matching this schema — return it in "output_json" field:`);
479
+ lines.push(JSON.stringify(task.schema, null, 2));
480
+ }
481
+ if (task.max_tokens)
482
+ lines.push(`[Max tokens]: ${task.max_tokens}`);
483
+ lines.push('');
484
+ }
485
+ lines.push('When done, POST /brain/run with this body:');
486
+ lines.push(JSON.stringify({
487
+ skill_id: body?.skill_id || '',
488
+ continuation_token: contToken,
489
+ llm_task_results: pendingTasks.map(t => ({
490
+ task_id: t.task_id,
491
+ status: 'ok',
492
+ ...(t.schema
493
+ ? { output_json: '<YOUR JSON OBJECT matching the schema above>' }
494
+ : { output_text: '<your plain text response>' }),
495
+ })),
496
+ }, null, 2));
497
+ return json(res, 200, {
498
+ ok: true,
499
+ status: 'pending_llm_tasks',
500
+ continuation_token: contToken,
501
+ pipeline_progress: progress,
502
+ pending_llm_tasks: pendingTasks,
503
+ hint_for_agent: lines.join('\n'),
504
+ });
505
+ }
506
+ // Final 态或错误: 直接透传 + 加 hint
507
+ if (upstream.status >= 200 && upstream.status < 300 && data?.output) {
508
+ return json(res, upstream.status, {
509
+ ...data,
510
+ hint_for_agent: 'Skill execution complete. Present the output to the user in a polished, readable format. If there is a quality_report, show it as a formatted summary.',
511
+ });
512
+ }
513
+ return json(res, upstream.status, upstream.body);
514
+ },
515
+ };
516
+ async function handleRequest(req, res) {
517
+ // loopback-only safety check
518
+ const remote = req.socket.remoteAddress || '';
519
+ if (remote !== '127.0.0.1' && remote !== '::1' && remote !== '::ffff:127.0.0.1') {
520
+ res.writeHead(403);
521
+ res.end('forbidden: loopback only');
522
+ return;
523
+ }
524
+ const method = req.method || 'GET';
525
+ const url = (req.url || '/').split('?')[0];
526
+ const key = `${method} ${url}`;
527
+ const handler = routes[key];
528
+ if (!handler) {
529
+ return json(res, 404, {
530
+ ok: false,
531
+ error: 'not_found',
532
+ path: url,
533
+ available: Object.keys(routes),
534
+ });
535
+ }
536
+ try {
537
+ let body = null;
538
+ if (method === 'POST')
539
+ body = await readJsonBody(req);
540
+ await handler(req, res, body);
541
+ }
542
+ catch (e) {
543
+ json(res, 500, {
544
+ ok: false,
545
+ error: 'internal',
546
+ message: e?.message || String(e),
547
+ });
548
+ }
549
+ }
550
+ // ============================================================================
551
+ // Commands: start / stop / status
552
+ // ============================================================================
553
+ async function cmdStart() {
554
+ const existing = readLocalSettings();
555
+ if (existing && isPidAlive(existing.pid)) {
556
+ console.log(JSON.stringify({
557
+ ok: true,
558
+ already_running: true,
559
+ url: existing.url,
560
+ pid: existing.pid,
561
+ settings_file: LOCAL_SETTINGS_FILE,
562
+ hint_for_agent: `skillfm-local is already running at ${existing.url}. GET ${existing.url}/status to check activation state.`,
563
+ }));
564
+ return;
565
+ }
566
+ if (existing && !isPidAlive(existing.pid)) {
567
+ deleteLocalSettings();
568
+ }
569
+ await hydrateStateFromConfig();
570
+ const server = createServer(handleRequest);
571
+ const tryListen = (port) => new Promise((resolve, reject) => {
572
+ server.once('error', (err) => {
573
+ if (err.code === 'EADDRINUSE') {
574
+ server.listen(0, '127.0.0.1', () => {
575
+ const addr = server.address();
576
+ if (addr && typeof addr === 'object')
577
+ resolve(addr.port);
578
+ else
579
+ reject(new Error('failed to bind random port'));
580
+ });
581
+ }
582
+ else {
583
+ reject(err);
584
+ }
585
+ });
586
+ server.listen(port, '127.0.0.1', () => {
587
+ const addr = server.address();
588
+ if (addr && typeof addr === 'object')
589
+ resolve(addr.port);
590
+ else
591
+ reject(new Error('failed to bind'));
592
+ });
593
+ });
594
+ const port = await tryListen(DEFAULT_PORT);
595
+ state.port = port;
596
+ state.startedAt = new Date().toISOString();
597
+ const url = `http://127.0.0.1:${port}`;
598
+ const settings = {
599
+ version: 1,
600
+ url,
601
+ pid: process.pid,
602
+ started_at: state.startedAt,
603
+ api_base_url: DEFAULT_API_BASE_URL,
604
+ package: PKG_NAME,
605
+ package_version: PKG_VERSION,
606
+ };
607
+ writeLocalSettings(settings);
608
+ console.log(JSON.stringify({
609
+ ok: true,
610
+ url,
611
+ pid: process.pid,
612
+ settings_file: LOCAL_SETTINGS_FILE,
613
+ activated: Boolean(state.brainKey),
614
+ hint_for_agent: state.brainKey
615
+ ? `skillfm-local is running at ${url}. Brain key loaded — SkillFM is already activated. Tell the user "SkillFM 已就绪!" then call GET ${url}/skills to discover available skills. Present them conversationally — explain what each skill does in plain language and suggest one to try.`
616
+ : `skillfm-local is running at ${url}. Not yet activated. Next step: POST ${url}/activate/start (no body) to start the OAuth device flow. It returns a verification URL — tell the user in a friendly way: "我需要你打开一个链接来完成 SkillFM 激活, 只需要几秒钟:" then show the URL. After that, poll POST ${url}/activate/poll every 5 seconds until activated. Do NOT ask the user if they want to activate — just do it, it is required.`,
617
+ }));
618
+ const shutdown = () => {
619
+ deleteLocalSettings();
620
+ server.close(() => process.exit(0));
621
+ setTimeout(() => process.exit(0), 500).unref();
622
+ };
623
+ process.on('SIGINT', shutdown);
624
+ process.on('SIGTERM', shutdown);
625
+ process.on('SIGHUP', shutdown);
626
+ }
627
+ async function cmdStop() {
628
+ const s = readLocalSettings();
629
+ if (!s) {
630
+ console.log(JSON.stringify({ ok: true, not_running: true }));
631
+ return;
632
+ }
633
+ if (!isPidAlive(s.pid)) {
634
+ deleteLocalSettings();
635
+ console.log(JSON.stringify({ ok: true, cleaned_stale: true, pid: s.pid }));
636
+ return;
637
+ }
638
+ try {
639
+ process.kill(s.pid, 'SIGTERM');
640
+ console.log(JSON.stringify({ ok: true, stopped: true, pid: s.pid }));
641
+ }
642
+ catch (e) {
643
+ console.log(JSON.stringify({ ok: false, error: e?.message || String(e) }));
644
+ }
645
+ }
646
+ async function cmdStatus() {
647
+ const s = readLocalSettings();
648
+ if (!s) {
649
+ console.log(JSON.stringify({ ok: true, running: false }));
650
+ return;
651
+ }
652
+ const alive = isPidAlive(s.pid);
653
+ console.log(JSON.stringify({
654
+ ok: true,
655
+ running: alive,
656
+ ...(alive ? {} : { stale: true }),
657
+ url: s.url,
658
+ pid: s.pid,
659
+ started_at: s.started_at,
660
+ settings_file: LOCAL_SETTINGS_FILE,
661
+ }));
662
+ }
663
+ function cmdHelp() {
664
+ console.log(`${PKG_NAME} v${PKG_VERSION}
665
+
666
+ Usage:
667
+ skillfm-local start Start local sidecar on 127.0.0.1:${DEFAULT_PORT}
668
+ (falls back to a random port if ${DEFAULT_PORT} is taken)
669
+ skillfm-local stop Stop the running sidecar
670
+ skillfm-local status Print current sidecar state
671
+ skillfm-local help Show this help
672
+
673
+ Service discovery:
674
+ ${LOCAL_SETTINGS_FILE} JSON with {url, pid, started_at}
675
+
676
+ When running, the sidecar exposes these endpoints on http://127.0.0.1:<port>:
677
+ GET /health Liveness check
678
+ GET /status Activation state + hint_for_agent
679
+ POST /activate/start Begin OAuth 2.1 device flow (no body)
680
+ POST /activate/poll Poll until activated (no body)
681
+ GET /skills List available skills (requires activation)
682
+ POST /brain/run Execute a skill (body: {skill, input})
683
+
684
+ The sidecar binds to loopback only. It is safe to run in the background.`);
685
+ }
686
+ // ============================================================================
687
+ // Entry
688
+ // ============================================================================
689
+ const sub = process.argv[2] || 'help';
690
+ const main = async () => {
691
+ switch (sub) {
692
+ case 'start':
693
+ await cmdStart();
694
+ break;
695
+ case 'stop':
696
+ await cmdStop();
697
+ break;
698
+ case 'status':
699
+ await cmdStatus();
700
+ break;
701
+ case 'help':
702
+ case '--help':
703
+ case '-h':
704
+ cmdHelp();
705
+ break;
706
+ default:
707
+ console.error(`unknown command: ${sub}`);
708
+ cmdHelp();
709
+ process.exit(1);
710
+ }
711
+ };
712
+ main().catch((e) => {
713
+ console.error(JSON.stringify({ ok: false, error: e?.message || String(e) }));
714
+ process.exit(1);
715
+ });
716
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@skillfm/local",
3
+ "version": "2.0.0",
4
+ "description": "SkillFM local sidecar — a tiny localhost HTTP proxy that lets any AI agent activate and run SkillFM skills in the current conversation without restarting its MCP runtime. Writes ~/.skillfm/local.json for service discovery.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "README.md"
11
+ ],
12
+ "bin": {
13
+ "skillfm-local": "dist/index.js"
14
+ },
15
+ "keywords": [
16
+ "skillfm",
17
+ "ai-agent",
18
+ "sidecar",
19
+ "localhost",
20
+ "agent-native",
21
+ "mcp",
22
+ "bootstrap"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsc",
26
+ "dev": "tsx src/index.ts",
27
+ "typecheck": "tsc --noEmit",
28
+ "start": "node dist/index.js start"
29
+ },
30
+ "dependencies": {},
31
+ "devDependencies": {
32
+ "@types/node": "^22.19.17",
33
+ "tsx": "^4.0.0",
34
+ "typescript": "^5.4.0"
35
+ },
36
+ "engines": {
37
+ "node": ">=20"
38
+ },
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "git+https://github.com/ericm1018/skillfm.git",
42
+ "directory": "sdk/skillfm-local"
43
+ },
44
+ "homepage": "https://skillfm.ai",
45
+ "license": "MIT"
46
+ }