@skillfm/local 2.6.4 → 2.7.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 +50 -1
- package/dist/_signers/aliyun-v3.d.ts +21 -0
- package/dist/_signers/aliyun-v3.d.ts.map +1 -0
- package/dist/_signers/aliyun-v3.js +47 -0
- package/dist/_signers/aliyun-v3.js.map +1 -0
- package/dist/_signers/canonical.d.ts +29 -0
- package/dist/_signers/canonical.d.ts.map +1 -0
- package/dist/_signers/canonical.js +59 -0
- package/dist/_signers/canonical.js.map +1 -0
- package/dist/_signers/index.d.ts +9 -0
- package/dist/_signers/index.d.ts.map +1 -0
- package/dist/_signers/index.js +6 -0
- package/dist/_signers/index.js.map +1 -0
- package/dist/_signers/tencent-tc3.d.ts +21 -0
- package/dist/_signers/tencent-tc3.d.ts.map +1 -0
- package/dist/_signers/tencent-tc3.js +48 -0
- package/dist/_signers/tencent-tc3.js.map +1 -0
- package/dist/_signers/volc-v4.d.ts +18 -0
- package/dist/_signers/volc-v4.d.ts.map +1 -0
- package/dist/_signers/volc-v4.js +43 -0
- package/dist/_signers/volc-v4.js.map +1 -0
- package/dist/asset-connectors/aliyun-ecs.d.ts +18 -0
- package/dist/asset-connectors/aliyun-ecs.d.ts.map +1 -0
- package/dist/asset-connectors/aliyun-ecs.js +99 -0
- package/dist/asset-connectors/aliyun-ecs.js.map +1 -0
- package/dist/asset-connectors/base.d.ts +15 -0
- package/dist/asset-connectors/base.d.ts.map +1 -0
- package/dist/asset-connectors/base.js +19 -0
- package/dist/asset-connectors/base.js.map +1 -0
- package/dist/asset-connectors/cloudflare.d.ts +18 -0
- package/dist/asset-connectors/cloudflare.d.ts.map +1 -0
- package/dist/asset-connectors/cloudflare.js +94 -0
- package/dist/asset-connectors/cloudflare.js.map +1 -0
- package/dist/asset-connectors/digitalocean.d.ts +18 -0
- package/dist/asset-connectors/digitalocean.d.ts.map +1 -0
- package/dist/asset-connectors/digitalocean.js +79 -0
- package/dist/asset-connectors/digitalocean.js.map +1 -0
- package/dist/asset-connectors/godaddy.d.ts +18 -0
- package/dist/asset-connectors/godaddy.d.ts.map +1 -0
- package/dist/asset-connectors/godaddy.js +62 -0
- package/dist/asset-connectors/godaddy.js.map +1 -0
- package/dist/asset-connectors/hetzner.d.ts +18 -0
- package/dist/asset-connectors/hetzner.d.ts.map +1 -0
- package/dist/asset-connectors/hetzner.js +52 -0
- package/dist/asset-connectors/hetzner.js.map +1 -0
- package/dist/asset-connectors/index.d.ts +18 -0
- package/dist/asset-connectors/index.d.ts.map +1 -0
- package/dist/asset-connectors/index.js +52 -0
- package/dist/asset-connectors/index.js.map +1 -0
- package/dist/asset-connectors/namecheap.d.ts +34 -0
- package/dist/asset-connectors/namecheap.d.ts.map +1 -0
- package/dist/asset-connectors/namecheap.js +178 -0
- package/dist/asset-connectors/namecheap.js.map +1 -0
- package/dist/asset-connectors/ssl-labs.d.ts +22 -0
- package/dist/asset-connectors/ssl-labs.d.ts.map +1 -0
- package/dist/asset-connectors/ssl-labs.js +79 -0
- package/dist/asset-connectors/ssl-labs.js.map +1 -0
- package/dist/asset-connectors/tencent-cvm.d.ts +18 -0
- package/dist/asset-connectors/tencent-cvm.d.ts.map +1 -0
- package/dist/asset-connectors/tencent-cvm.js +145 -0
- package/dist/asset-connectors/tencent-cvm.js.map +1 -0
- package/dist/asset-connectors/types.d.ts +45 -0
- package/dist/asset-connectors/types.d.ts.map +1 -0
- package/dist/asset-connectors/types.js +26 -0
- package/dist/asset-connectors/types.js.map +1 -0
- package/dist/asset-connectors/vultr.d.ts +18 -0
- package/dist/asset-connectors/vultr.d.ts.map +1 -0
- package/dist/asset-connectors/vultr.js +73 -0
- package/dist/asset-connectors/vultr.js.map +1 -0
- package/dist/connectors/anthropic.d.ts +10 -0
- package/dist/connectors/anthropic.d.ts.map +1 -0
- package/dist/connectors/anthropic.js +103 -0
- package/dist/connectors/anthropic.js.map +1 -0
- package/dist/connectors/base.d.ts +11 -0
- package/dist/connectors/base.d.ts.map +1 -0
- package/dist/connectors/base.js +19 -0
- package/dist/connectors/base.js.map +1 -0
- package/dist/connectors/deepseek.d.ts +14 -0
- package/dist/connectors/deepseek.d.ts.map +1 -0
- package/dist/connectors/deepseek.js +77 -0
- package/dist/connectors/deepseek.js.map +1 -0
- package/dist/connectors/doubao.d.ts +10 -0
- package/dist/connectors/doubao.d.ts.map +1 -0
- package/dist/connectors/doubao.js +64 -0
- package/dist/connectors/doubao.js.map +1 -0
- package/dist/connectors/index.d.ts +14 -0
- package/dist/connectors/index.d.ts.map +1 -0
- package/dist/connectors/index.js +42 -0
- package/dist/connectors/index.js.map +1 -0
- package/dist/connectors/kimi.d.ts +10 -0
- package/dist/connectors/kimi.d.ts.map +1 -0
- package/dist/connectors/kimi.js +67 -0
- package/dist/connectors/kimi.js.map +1 -0
- package/dist/connectors/openai.d.ts +10 -0
- package/dist/connectors/openai.d.ts.map +1 -0
- package/dist/connectors/openai.js +101 -0
- package/dist/connectors/openai.js.map +1 -0
- package/dist/connectors/qwen.d.ts +10 -0
- package/dist/connectors/qwen.d.ts.map +1 -0
- package/dist/connectors/qwen.js +66 -0
- package/dist/connectors/qwen.js.map +1 -0
- package/dist/connectors/types.d.ts +47 -0
- package/dist/connectors/types.d.ts.map +1 -0
- package/dist/connectors/types.js +24 -0
- package/dist/connectors/types.js.map +1 -0
- package/dist/guard/bin.js +0 -0
- package/dist/index.js +50 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/_vendored-contracts/beacon-mcp-tools-v1.d.ts +373 -0
- package/dist/mcp/_vendored-contracts/beacon-mcp-tools-v1.d.ts.map +1 -0
- package/dist/mcp/_vendored-contracts/beacon-mcp-tools-v1.js +128 -0
- package/dist/mcp/_vendored-contracts/beacon-mcp-tools-v1.js.map +1 -0
- package/dist/mcp/brain-client.d.ts +96 -0
- package/dist/mcp/brain-client.d.ts.map +1 -0
- package/dist/mcp/brain-client.js +214 -0
- package/dist/mcp/brain-client.js.map +1 -0
- package/dist/mcp/cache.d.ts +55 -0
- package/dist/mcp/cache.d.ts.map +1 -0
- package/dist/mcp/cache.js +109 -0
- package/dist/mcp/cache.js.map +1 -0
- package/dist/mcp/config.d.ts +29 -0
- package/dist/mcp/config.d.ts.map +1 -0
- package/dist/mcp/config.js +69 -0
- package/dist/mcp/config.js.map +1 -0
- package/dist/mcp/decision-engine.d.ts +54 -0
- package/dist/mcp/decision-engine.d.ts.map +1 -0
- package/dist/mcp/decision-engine.js +107 -0
- package/dist/mcp/decision-engine.js.map +1 -0
- package/dist/mcp/errors.d.ts +66 -0
- package/dist/mcp/errors.d.ts.map +1 -0
- package/dist/mcp/errors.js +127 -0
- package/dist/mcp/errors.js.map +1 -0
- package/dist/mcp/index.d.ts +40 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +98 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/tools/_placeholder.d.ts +24 -0
- package/dist/mcp/tools/_placeholder.d.ts.map +1 -0
- package/dist/mcp/tools/_placeholder.js +29 -0
- package/dist/mcp/tools/_placeholder.js.map +1 -0
- package/dist/mcp/tools/avatar-market.d.ts +22 -0
- package/dist/mcp/tools/avatar-market.d.ts.map +1 -0
- package/dist/mcp/tools/avatar-market.js +33 -0
- package/dist/mcp/tools/avatar-market.js.map +1 -0
- package/dist/mcp/tools/check-expiry.d.ts +23 -0
- package/dist/mcp/tools/check-expiry.d.ts.map +1 -0
- package/dist/mcp/tools/check-expiry.js +29 -0
- package/dist/mcp/tools/check-expiry.js.map +1 -0
- package/dist/mcp/tools/check-usage.d.ts +25 -0
- package/dist/mcp/tools/check-usage.d.ts.map +1 -0
- package/dist/mcp/tools/check-usage.js +33 -0
- package/dist/mcp/tools/check-usage.js.map +1 -0
- package/dist/mcp/tools/get-monthly-picks.d.ts +25 -0
- package/dist/mcp/tools/get-monthly-picks.d.ts.map +1 -0
- package/dist/mcp/tools/get-monthly-picks.js +35 -0
- package/dist/mcp/tools/get-monthly-picks.js.map +1 -0
- package/dist/mcp/tools/index.d.ts +41 -0
- package/dist/mcp/tools/index.d.ts.map +1 -0
- package/dist/mcp/tools/index.js +139 -0
- package/dist/mcp/tools/index.js.map +1 -0
- package/dist/mcp/tools/memory-recent.d.ts +17 -0
- package/dist/mcp/tools/memory-recent.d.ts.map +1 -0
- package/dist/mcp/tools/memory-recent.js +46 -0
- package/dist/mcp/tools/memory-recent.js.map +1 -0
- package/dist/mcp/tools/memory-save.d.ts +15 -0
- package/dist/mcp/tools/memory-save.d.ts.map +1 -0
- package/dist/mcp/tools/memory-save.js +53 -0
- package/dist/mcp/tools/memory-save.js.map +1 -0
- package/dist/mcp/tools/memory-search.d.ts +24 -0
- package/dist/mcp/tools/memory-search.d.ts.map +1 -0
- package/dist/mcp/tools/memory-search.js +64 -0
- package/dist/mcp/tools/memory-search.js.map +1 -0
- package/dist/mcp/tools/record-acceptance.d.ts +22 -0
- package/dist/mcp/tools/record-acceptance.d.ts.map +1 -0
- package/dist/mcp/tools/record-acceptance.js +33 -0
- package/dist/mcp/tools/record-acceptance.js.map +1 -0
- package/dist/mcp/tools/record-rejection.d.ts +23 -0
- package/dist/mcp/tools/record-rejection.d.ts.map +1 -0
- package/dist/mcp/tools/record-rejection.js +34 -0
- package/dist/mcp/tools/record-rejection.js.map +1 -0
- package/dist/mcp/tools/suggest-route.d.ts +26 -0
- package/dist/mcp/tools/suggest-route.d.ts.map +1 -0
- package/dist/mcp/tools/suggest-route.js +56 -0
- package/dist/mcp/tools/suggest-route.js.map +1 -0
- package/dist/mcp/tools/user-preferences.d.ts +22 -0
- package/dist/mcp/tools/user-preferences.d.ts.map +1 -0
- package/dist/mcp/tools/user-preferences.js +63 -0
- package/dist/mcp/tools/user-preferences.js.map +1 -0
- package/dist/mcp-stdio/bin.js +0 -0
- package/dist/memory/index.d.ts +5 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +4 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/mem0-client.d.ts +32 -0
- package/dist/memory/mem0-client.d.ts.map +1 -0
- package/dist/memory/mem0-client.js +161 -0
- package/dist/memory/mem0-client.js.map +1 -0
- package/dist/memory/types.d.ts +47 -0
- package/dist/memory/types.d.ts.map +1 -0
- package/dist/memory/types.js +20 -0
- package/dist/memory/types.js.map +1 -0
- package/dist/router/catalog.d.ts +4 -0
- package/dist/router/catalog.d.ts.map +1 -0
- package/dist/router/catalog.js +103 -0
- package/dist/router/catalog.js.map +1 -0
- package/dist/router/classifier.d.ts +5 -0
- package/dist/router/classifier.d.ts.map +1 -0
- package/dist/router/classifier.js +43 -0
- package/dist/router/classifier.js.map +1 -0
- package/dist/router/index.d.ts +7 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router/index.js +6 -0
- package/dist/router/index.js.map +1 -0
- package/dist/router/litellm-config.d.ts +34 -0
- package/dist/router/litellm-config.d.ts.map +1 -0
- package/dist/router/litellm-config.js +78 -0
- package/dist/router/litellm-config.js.map +1 -0
- package/dist/router/route.d.ts +3 -0
- package/dist/router/route.d.ts.map +1 -0
- package/dist/router/route.js +63 -0
- package/dist/router/route.js.map +1 -0
- package/dist/router/types.d.ts +36 -0
- package/dist/router/types.d.ts.map +1 -0
- package/dist/router/types.js +7 -0
- package/dist/router/types.js.map +1 -0
- package/dist/saas-connectors/base.d.ts +16 -0
- package/dist/saas-connectors/base.d.ts.map +1 -0
- package/dist/saas-connectors/base.js +16 -0
- package/dist/saas-connectors/base.js.map +1 -0
- package/dist/saas-connectors/brave.d.ts +19 -0
- package/dist/saas-connectors/brave.d.ts.map +1 -0
- package/dist/saas-connectors/brave.js +50 -0
- package/dist/saas-connectors/brave.js.map +1 -0
- package/dist/saas-connectors/fal.d.ts +15 -0
- package/dist/saas-connectors/fal.d.ts.map +1 -0
- package/dist/saas-connectors/fal.js +35 -0
- package/dist/saas-connectors/fal.js.map +1 -0
- package/dist/saas-connectors/index.d.ts +11 -0
- package/dist/saas-connectors/index.d.ts.map +1 -0
- package/dist/saas-connectors/index.js +32 -0
- package/dist/saas-connectors/index.js.map +1 -0
- package/dist/saas-connectors/replicate.d.ts +19 -0
- package/dist/saas-connectors/replicate.d.ts.map +1 -0
- package/dist/saas-connectors/replicate.js +43 -0
- package/dist/saas-connectors/replicate.js.map +1 -0
- package/dist/saas-connectors/serper.d.ts +19 -0
- package/dist/saas-connectors/serper.d.ts.map +1 -0
- package/dist/saas-connectors/serper.js +47 -0
- package/dist/saas-connectors/serper.js.map +1 -0
- package/dist/saas-connectors/types.d.ts +31 -0
- package/dist/saas-connectors/types.d.ts.map +1 -0
- package/dist/saas-connectors/types.js +6 -0
- package/dist/saas-connectors/types.js.map +1 -0
- package/dist/scheduler/index.d.ts +4 -0
- package/dist/scheduler/index.d.ts.map +1 -0
- package/dist/scheduler/index.js +4 -0
- package/dist/scheduler/index.js.map +1 -0
- package/dist/scheduler/poll.d.ts +4 -0
- package/dist/scheduler/poll.d.ts.map +1 -0
- package/dist/scheduler/poll.js +170 -0
- package/dist/scheduler/poll.js.map +1 -0
- package/dist/scheduler/scheduler.d.ts +18 -0
- package/dist/scheduler/scheduler.d.ts.map +1 -0
- package/dist/scheduler/scheduler.js +67 -0
- package/dist/scheduler/scheduler.js.map +1 -0
- package/dist/scheduler/types.d.ts +49 -0
- package/dist/scheduler/types.d.ts.map +1 -0
- package/dist/scheduler/types.js +14 -0
- package/dist/scheduler/types.js.map +1 -0
- package/dist/vault/cli.d.ts +2 -0
- package/dist/vault/cli.d.ts.map +1 -0
- package/dist/vault/cli.js +170 -0
- package/dist/vault/cli.js.map +1 -0
- package/dist/vault/crypto.d.ts +9 -0
- package/dist/vault/crypto.d.ts.map +1 -0
- package/dist/vault/crypto.js +54 -0
- package/dist/vault/crypto.js.map +1 -0
- package/dist/vault/index.d.ts +6 -0
- package/dist/vault/index.d.ts.map +1 -0
- package/dist/vault/index.js +5 -0
- package/dist/vault/index.js.map +1 -0
- package/dist/vault/types.d.ts +28 -0
- package/dist/vault/types.d.ts.map +1 -0
- package/dist/vault/types.js +22 -0
- package/dist/vault/types.js.map +1 -0
- package/dist/vault/vault.d.ts +34 -0
- package/dist/vault/vault.d.ts.map +1 -0
- package/dist/vault/vault.js +149 -0
- package/dist/vault/vault.js.map +1 -0
- package/package.json +17 -5
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/config.ts — Read agent token + API base URL.
|
|
3
|
+
*
|
|
4
|
+
* Resolution order:
|
|
5
|
+
* 1. process.env.SKILLFM_AGENT_TOKEN
|
|
6
|
+
* 2. ~/.skillfm/beacon-mcp/config.json `{ agentToken, apiBaseUrl }`
|
|
7
|
+
* 3. Fallback → degraded=true (no token, public/cached endpoints only)
|
|
8
|
+
*
|
|
9
|
+
* `degraded` propagates to the MCP server: every tool can still run but
|
|
10
|
+
* brain-backed paths fall back to local cache or NOT_IMPLEMENTED.
|
|
11
|
+
*
|
|
12
|
+
* TODO Week 5:
|
|
13
|
+
* - Replace ad-hoc JSON config with the same `~/.skillfm/config.json` schema
|
|
14
|
+
* that `@skillfm/local` writes (already vault-protected). Currently the two
|
|
15
|
+
* packages each own a slice of the homedir.
|
|
16
|
+
* - Add `SKILLFM_DRY_RUN=1` knob so smoke tests never call brain-client.
|
|
17
|
+
* - Plug into `skillfm-beacon-mcp bind` CLI subcommand (bin.ts) for
|
|
18
|
+
* OAuth device-flow binding (mirror @skillfm/local activate flow).
|
|
19
|
+
*/
|
|
20
|
+
export declare const DEFAULT_API_BASE_URL = "https://api.skillfm.ai";
|
|
21
|
+
export interface BeaconConfig {
|
|
22
|
+
agentToken: string | null;
|
|
23
|
+
apiBaseUrl: string;
|
|
24
|
+
degraded: boolean;
|
|
25
|
+
}
|
|
26
|
+
export declare function loadConfig(): BeaconConfig;
|
|
27
|
+
/** Used by tests to assert resolution precedence; not exported for runtime. */
|
|
28
|
+
export declare function _resolveHomePath(): string;
|
|
29
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/mcp/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAQH,eAAO,MAAM,oBAAoB,2BAA2B,CAAC;AAE7D,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAwBD,wBAAgB,UAAU,IAAI,YAAY,CAwBzC;AAED,+EAA+E;AAC/E,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/config.ts — Read agent token + API base URL.
|
|
3
|
+
*
|
|
4
|
+
* Resolution order:
|
|
5
|
+
* 1. process.env.SKILLFM_AGENT_TOKEN
|
|
6
|
+
* 2. ~/.skillfm/beacon-mcp/config.json `{ agentToken, apiBaseUrl }`
|
|
7
|
+
* 3. Fallback → degraded=true (no token, public/cached endpoints only)
|
|
8
|
+
*
|
|
9
|
+
* `degraded` propagates to the MCP server: every tool can still run but
|
|
10
|
+
* brain-backed paths fall back to local cache or NOT_IMPLEMENTED.
|
|
11
|
+
*
|
|
12
|
+
* TODO Week 5:
|
|
13
|
+
* - Replace ad-hoc JSON config with the same `~/.skillfm/config.json` schema
|
|
14
|
+
* that `@skillfm/local` writes (already vault-protected). Currently the two
|
|
15
|
+
* packages each own a slice of the homedir.
|
|
16
|
+
* - Add `SKILLFM_DRY_RUN=1` knob so smoke tests never call brain-client.
|
|
17
|
+
* - Plug into `skillfm-beacon-mcp bind` CLI subcommand (bin.ts) for
|
|
18
|
+
* OAuth device-flow binding (mirror @skillfm/local activate flow).
|
|
19
|
+
*/
|
|
20
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
21
|
+
import { join } from 'node:path';
|
|
22
|
+
import { homedir } from 'node:os';
|
|
23
|
+
import { BEACON_HOME } from './cache.js';
|
|
24
|
+
export const DEFAULT_API_BASE_URL = 'https://api.skillfm.ai';
|
|
25
|
+
function readConfigFile() {
|
|
26
|
+
const path = join(BEACON_HOME, 'config.json');
|
|
27
|
+
if (!existsSync(path))
|
|
28
|
+
return null;
|
|
29
|
+
try {
|
|
30
|
+
const raw = readFileSync(path, 'utf8');
|
|
31
|
+
const parsed = JSON.parse(raw);
|
|
32
|
+
if (parsed && typeof parsed === 'object') {
|
|
33
|
+
return parsed;
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
// never throw from config loader — log to stderr (stdout is MCP transport)
|
|
39
|
+
console.error('[skillfm-beacon-mcp] config.json read failed:', err.message);
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export function loadConfig() {
|
|
44
|
+
const envToken = process.env.SKILLFM_AGENT_TOKEN?.trim();
|
|
45
|
+
const envBase = process.env.SKILLFM_API_BASE_URL?.trim();
|
|
46
|
+
let token = envToken && envToken.length > 0 ? envToken : null;
|
|
47
|
+
let baseUrl = envBase && envBase.length > 0 ? envBase : DEFAULT_API_BASE_URL;
|
|
48
|
+
if (!token || !envBase) {
|
|
49
|
+
const file = readConfigFile();
|
|
50
|
+
if (file) {
|
|
51
|
+
if (!token && typeof file.agentToken === 'string' && file.agentToken.length > 0) {
|
|
52
|
+
token = file.agentToken;
|
|
53
|
+
}
|
|
54
|
+
if (!envBase && typeof file.apiBaseUrl === 'string' && file.apiBaseUrl.length > 0) {
|
|
55
|
+
baseUrl = file.apiBaseUrl;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
agentToken: token,
|
|
61
|
+
apiBaseUrl: baseUrl,
|
|
62
|
+
degraded: token === null,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/** Used by tests to assert resolution precedence; not exported for runtime. */
|
|
66
|
+
export function _resolveHomePath() {
|
|
67
|
+
return join(homedir(), '.skillfm', 'beacon-mcp');
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/mcp/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,CAAC,MAAM,oBAAoB,GAAG,wBAAwB,CAAC;AAa7D,SAAS,cAAc;IACrB,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;IAC9C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACvC,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YACzC,OAAO,MAAyB,CAAC;QACnC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,2EAA2E;QAC3E,OAAO,CAAC,KAAK,CAAC,+CAA+C,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC;QACvF,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,IAAI,EAAE,CAAC;IACzD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,IAAI,EAAE,CAAC;IAEzD,IAAI,KAAK,GAAkB,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7E,IAAI,OAAO,GAAW,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC;IAErF,IAAI,CAAC,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;QAC9B,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,KAAK,IAAI,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAChF,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC;YAC1B,CAAC;YACD,IAAI,CAAC,OAAO,IAAI,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAClF,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC;YAC5B,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,UAAU,EAAE,KAAK;QACjB,UAAU,EAAE,OAAO;QACnB,QAAQ,EAAE,KAAK,KAAK,IAAI;KACzB,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,gBAAgB;IAC9B,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC;AACnD,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/decision-engine.ts — Client-side decision tree (PRD-MCP-SKILL §5.3).
|
|
3
|
+
*
|
|
4
|
+
* Decides whether to surface a suggestion to the user, *before* hitting brain.
|
|
5
|
+
* This is the layer that protects users from Clippy-style spam:
|
|
6
|
+
*
|
|
7
|
+
* 1. Frequency limit — `userPrefs.maxPerSession` / `maxPerDay`
|
|
8
|
+
* 2. Quiet hours — `userPrefs.quietHours`
|
|
9
|
+
* 3. Disabled category — `userPrefs.disabledCategories[A..H]`
|
|
10
|
+
* 4. Duplicate suggestion — same trigger key inside dedupe window
|
|
11
|
+
* 5. Brain rule fetch — only after the above 4 pass
|
|
12
|
+
*
|
|
13
|
+
* Phase 0 implements 1–4 as real logic (these are 100% client invariants and
|
|
14
|
+
* don't depend on brain). Step 5 calls BrainClient.fetchRule which currently
|
|
15
|
+
* returns mock data — fine, the wiring is already correct.
|
|
16
|
+
*
|
|
17
|
+
* TODO Week 5:
|
|
18
|
+
* - Wire real per-day counter into session-cache.db (Phase 0 only counts per
|
|
19
|
+
* session — restart of MCP server resets the per-day counter).
|
|
20
|
+
* - Honor `forceShow` override for high-severity expiry alerts (PRD §5.4).
|
|
21
|
+
* - Plug A/B-test bucket id into context hash so brain can split-test rules.
|
|
22
|
+
* - Emit OpenTelemetry span for every decision — saves debug time when
|
|
23
|
+
* users complain "SkillFM never suggests anything".
|
|
24
|
+
*/
|
|
25
|
+
import { type TriggerType, type UserPreferences } from './_vendored-contracts/beacon-mcp-tools-v1.js';
|
|
26
|
+
import type { BrainClient } from './brain-client.js';
|
|
27
|
+
import type { SessionCacheHandle } from './cache.js';
|
|
28
|
+
export interface SuggestionDecision {
|
|
29
|
+
/** True if the agent should surface this to the user. */
|
|
30
|
+
shouldSuggest: boolean;
|
|
31
|
+
/** Human-readable reason if shouldSuggest=false. */
|
|
32
|
+
reason?: string;
|
|
33
|
+
/** Domain code if shouldSuggest=false (matches SKILLFM_ERROR_CODES). */
|
|
34
|
+
reasonCode?: string;
|
|
35
|
+
/** Brain-issued rule id (only present when shouldSuggest=true). */
|
|
36
|
+
ruleId?: string;
|
|
37
|
+
/** Opaque rule payload for the agent to render. */
|
|
38
|
+
rulePayload?: Record<string, unknown>;
|
|
39
|
+
}
|
|
40
|
+
export interface EvaluateOpts {
|
|
41
|
+
triggerType: TriggerType;
|
|
42
|
+
triggerContext: Record<string, unknown>;
|
|
43
|
+
userPrefs: UserPreferences;
|
|
44
|
+
sessionCache: SessionCacheHandle;
|
|
45
|
+
brain: BrainClient;
|
|
46
|
+
/** ISO weekday + HH:mm for quiet-hours check; defaults to `new Date()`. */
|
|
47
|
+
now?: Date;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Phase 0 decision tree — runs locally, calls brain only on the happy path.
|
|
51
|
+
* Returns a `SuggestionDecision` envelope every time (never throws).
|
|
52
|
+
*/
|
|
53
|
+
export declare function evaluateSuggestion(opts: EvaluateOpts): Promise<SuggestionDecision>;
|
|
54
|
+
//# sourceMappingURL=decision-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision-engine.d.ts","sourceRoot":"","sources":["../../src/mcp/decision-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,eAAe,EAErB,MAAM,8CAA8C,CAAC;AAEtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAKrD,MAAM,WAAW,kBAAkB;IACjC,yDAAyD;IACzD,aAAa,EAAE,OAAO,CAAC;IACvB,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,WAAW,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,SAAS,EAAE,eAAe,CAAC;IAC3B,YAAY,EAAE,kBAAkB,CAAC;IACjC,KAAK,EAAE,WAAW,CAAC;IACnB,2EAA2E;IAC3E,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAuBD;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA2DxF"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/decision-engine.ts — Client-side decision tree (PRD-MCP-SKILL §5.3).
|
|
3
|
+
*
|
|
4
|
+
* Decides whether to surface a suggestion to the user, *before* hitting brain.
|
|
5
|
+
* This is the layer that protects users from Clippy-style spam:
|
|
6
|
+
*
|
|
7
|
+
* 1. Frequency limit — `userPrefs.maxPerSession` / `maxPerDay`
|
|
8
|
+
* 2. Quiet hours — `userPrefs.quietHours`
|
|
9
|
+
* 3. Disabled category — `userPrefs.disabledCategories[A..H]`
|
|
10
|
+
* 4. Duplicate suggestion — same trigger key inside dedupe window
|
|
11
|
+
* 5. Brain rule fetch — only after the above 4 pass
|
|
12
|
+
*
|
|
13
|
+
* Phase 0 implements 1–4 as real logic (these are 100% client invariants and
|
|
14
|
+
* don't depend on brain). Step 5 calls BrainClient.fetchRule which currently
|
|
15
|
+
* returns mock data — fine, the wiring is already correct.
|
|
16
|
+
*
|
|
17
|
+
* TODO Week 5:
|
|
18
|
+
* - Wire real per-day counter into session-cache.db (Phase 0 only counts per
|
|
19
|
+
* session — restart of MCP server resets the per-day counter).
|
|
20
|
+
* - Honor `forceShow` override for high-severity expiry alerts (PRD §5.4).
|
|
21
|
+
* - Plug A/B-test bucket id into context hash so brain can split-test rules.
|
|
22
|
+
* - Emit OpenTelemetry span for every decision — saves debug time when
|
|
23
|
+
* users complain "SkillFM never suggests anything".
|
|
24
|
+
*/
|
|
25
|
+
import { getCategoryFromTriggerType, } from './_vendored-contracts/beacon-mcp-tools-v1.js';
|
|
26
|
+
import { SKILLFM_ERROR_CODES } from './errors.js';
|
|
27
|
+
const DEFAULT_DEDUPE_WINDOW_MS = 30 * 60_000; // 30 minutes
|
|
28
|
+
function _isInQuietHours(prefs, now) {
|
|
29
|
+
if (!prefs.quietHours.enabled)
|
|
30
|
+
return false;
|
|
31
|
+
// NOTE Phase 0 ignores timezone — uses local clock. Week 5 will use Intl.
|
|
32
|
+
const hh = String(now.getHours()).padStart(2, '0');
|
|
33
|
+
const mm = String(now.getMinutes()).padStart(2, '0');
|
|
34
|
+
const cur = `${hh}:${mm}`;
|
|
35
|
+
const { start, end } = prefs.quietHours;
|
|
36
|
+
// Wrap-around (22:00 → 08:00): inside if cur >= start OR cur < end
|
|
37
|
+
if (start > end)
|
|
38
|
+
return cur >= start || cur < end;
|
|
39
|
+
return cur >= start && cur < end;
|
|
40
|
+
}
|
|
41
|
+
function _dedupeKey(triggerType, ctx) {
|
|
42
|
+
// Stable key: trigger + sorted-keys signature (cheap, no full hash needed Phase 0)
|
|
43
|
+
const sig = Object.keys(ctx)
|
|
44
|
+
.sort()
|
|
45
|
+
.map((k) => `${k}=${String(ctx[k] ?? '')}`)
|
|
46
|
+
.join('&');
|
|
47
|
+
return `${triggerType}::${sig}`;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Phase 0 decision tree — runs locally, calls brain only on the happy path.
|
|
51
|
+
* Returns a `SuggestionDecision` envelope every time (never throws).
|
|
52
|
+
*/
|
|
53
|
+
export async function evaluateSuggestion(opts) {
|
|
54
|
+
const { triggerType, triggerContext, userPrefs, sessionCache, brain } = opts;
|
|
55
|
+
const now = opts.now ?? new Date();
|
|
56
|
+
// 1. Disabled-category check
|
|
57
|
+
const cat = getCategoryFromTriggerType(triggerType);
|
|
58
|
+
if (cat !== null && userPrefs.disabledCategories.includes(cat)) {
|
|
59
|
+
return {
|
|
60
|
+
shouldSuggest: false,
|
|
61
|
+
reason: `User disabled suggestion category ${cat}.`,
|
|
62
|
+
reasonCode: SKILLFM_ERROR_CODES.DECISION_USER_OPTED_OUT,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
// 2. Quiet hours
|
|
66
|
+
if (_isInQuietHours(userPrefs, now)) {
|
|
67
|
+
return {
|
|
68
|
+
shouldSuggest: false,
|
|
69
|
+
reason: 'Inside user-configured quiet hours.',
|
|
70
|
+
reasonCode: SKILLFM_ERROR_CODES.DECISION_USER_OPTED_OUT,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// 3. Frequency limit (per-session)
|
|
74
|
+
if (sessionCache.sessionCount() >= userPrefs.maxPerSession) {
|
|
75
|
+
return {
|
|
76
|
+
shouldSuggest: false,
|
|
77
|
+
reason: `Reached maxPerSession=${userPrefs.maxPerSession}.`,
|
|
78
|
+
reasonCode: SKILLFM_ERROR_CODES.DECISION_FREQUENCY_LIMITED,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
// 4. Duplicate suggestion within dedupe window
|
|
82
|
+
const key = _dedupeKey(triggerType, triggerContext);
|
|
83
|
+
if (sessionCache.hasRecentSuggestion(key, DEFAULT_DEDUPE_WINDOW_MS)) {
|
|
84
|
+
return {
|
|
85
|
+
shouldSuggest: false,
|
|
86
|
+
reason: 'Duplicate suggestion within 30-min window.',
|
|
87
|
+
reasonCode: SKILLFM_ERROR_CODES.DECISION_DUPLICATE_SUGGESTION,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
// 5. Brain rule fetch (with implicit fallback to local cache via BrainClient)
|
|
91
|
+
const ruleResult = await brain.fetchRule(triggerType, triggerContext, {});
|
|
92
|
+
if (!ruleResult.ok) {
|
|
93
|
+
return {
|
|
94
|
+
shouldSuggest: false,
|
|
95
|
+
reason: ruleResult.message,
|
|
96
|
+
reasonCode: ruleResult.code,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
// Happy path — record + return
|
|
100
|
+
sessionCache.recordSuggestion(key);
|
|
101
|
+
return {
|
|
102
|
+
shouldSuggest: true,
|
|
103
|
+
ruleId: ruleResult.data.ruleId,
|
|
104
|
+
rulePayload: ruleResult.data.payload,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=decision-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision-engine.js","sourceRoot":"","sources":["../../src/mcp/decision-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/errors.ts — Error factory + domain error code constants.
|
|
3
|
+
*
|
|
4
|
+
* All MCP tool handlers in this package return either
|
|
5
|
+
* { ok: true, data, degraded? }
|
|
6
|
+
* or
|
|
7
|
+
* { ok: false, error: SkillFmToolError }
|
|
8
|
+
* (shape defined in the vendored contract `beacon-mcp-tools-v1.ts`).
|
|
9
|
+
*
|
|
10
|
+
* This file owns the canonical list of error codes. Codes are domain-prefixed
|
|
11
|
+
* `'SKILLFM.<DOMAIN>.<REASON>'` to make grep + log filtering reliable.
|
|
12
|
+
*
|
|
13
|
+
* TODO Week 5:
|
|
14
|
+
* - Wire codes into the brain-client retry classifier (retryable=true vs false)
|
|
15
|
+
* - Add i18n table for `userHint` (currently each handler ad-hoc fills en/zh)
|
|
16
|
+
* - Add a structured `cause` field (preserving original Error) once we have
|
|
17
|
+
* real network/io failures to surface — Phase 0 returns mocks, no real cause.
|
|
18
|
+
*/
|
|
19
|
+
import type { SkillFmToolError } from './_vendored-contracts/beacon-mcp-tools-v1.js';
|
|
20
|
+
export declare const SKILLFM_ERROR_CODES: {
|
|
21
|
+
readonly AUTH_NOT_BOUND: "SKILLFM.AUTH.NOT_BOUND";
|
|
22
|
+
readonly AUTH_INVALID_TOKEN: "SKILLFM.AUTH.INVALID_TOKEN";
|
|
23
|
+
readonly AUTH_PRO_REQUIRED: "SKILLFM.AUTH.PRO_REQUIRED";
|
|
24
|
+
readonly NETWORK_UNREACHABLE: "SKILLFM.NETWORK.UNREACHABLE";
|
|
25
|
+
readonly NETWORK_TIMEOUT: "SKILLFM.NETWORK.TIMEOUT";
|
|
26
|
+
readonly BRAIN_RATE_LIMITED: "SKILLFM.BRAIN.RATE_LIMITED";
|
|
27
|
+
readonly BRAIN_INTERNAL: "SKILLFM.BRAIN.INTERNAL";
|
|
28
|
+
readonly BRAIN_CONTRACT_MISMATCH: "SKILLFM.BRAIN.CONTRACT_MISMATCH";
|
|
29
|
+
readonly TOOL_NOT_IMPLEMENTED: "SKILLFM.TOOL.NOT_IMPLEMENTED";
|
|
30
|
+
readonly TOOL_INVALID_INPUT: "SKILLFM.TOOL.INVALID_INPUT";
|
|
31
|
+
readonly CACHE_READ_FAILED: "SKILLFM.CACHE.READ_FAILED";
|
|
32
|
+
readonly CACHE_WRITE_FAILED: "SKILLFM.CACHE.WRITE_FAILED";
|
|
33
|
+
readonly DECISION_FREQUENCY_LIMITED: "SKILLFM.DECISION.FREQUENCY_LIMITED";
|
|
34
|
+
readonly DECISION_USER_OPTED_OUT: "SKILLFM.DECISION.USER_OPTED_OUT";
|
|
35
|
+
readonly DECISION_DUPLICATE_SUGGESTION: "SKILLFM.DECISION.DUPLICATE_SUGGESTION";
|
|
36
|
+
};
|
|
37
|
+
export type SkillFmErrorCode = (typeof SKILLFM_ERROR_CODES)[keyof typeof SKILLFM_ERROR_CODES];
|
|
38
|
+
/**
|
|
39
|
+
* Throwable error class. Tool handlers can `throw new SkillFmError(...)`
|
|
40
|
+
* and the MCP server boundary will convert into `SkillFmToolError` envelope.
|
|
41
|
+
*/
|
|
42
|
+
export declare class SkillFmError extends Error {
|
|
43
|
+
readonly code: string;
|
|
44
|
+
readonly userHint?: {
|
|
45
|
+
en: string;
|
|
46
|
+
zh: string;
|
|
47
|
+
};
|
|
48
|
+
readonly retryable: boolean;
|
|
49
|
+
constructor(opts: {
|
|
50
|
+
code: string;
|
|
51
|
+
message: string;
|
|
52
|
+
userHint?: {
|
|
53
|
+
en: string;
|
|
54
|
+
zh: string;
|
|
55
|
+
};
|
|
56
|
+
retryable?: boolean;
|
|
57
|
+
});
|
|
58
|
+
/** Convert to the wire-shape error returned inside `SkillFmToolResult.error`. */
|
|
59
|
+
toToolError(): SkillFmToolError;
|
|
60
|
+
}
|
|
61
|
+
export declare function notImplemented(toolName: string): SkillFmError;
|
|
62
|
+
export declare function authNotBound(): SkillFmError;
|
|
63
|
+
export declare function networkUnreachable(detail: string): SkillFmError;
|
|
64
|
+
export declare function proRequired(featureName: string): SkillFmError;
|
|
65
|
+
export declare function invalidInput(reason: string): SkillFmError;
|
|
66
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/mcp/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8CAA8C,CAAC;AAMrF,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;CA2BtB,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAC1B,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,OAAO,mBAAmB,CAAC,CAAC;AAMjE;;;GAGG;AACH,qBAAa,YAAa,SAAQ,KAAK;IACrC,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,SAAgB,QAAQ,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACtD,SAAgB,SAAS,EAAE,OAAO,CAAC;gBAEvB,IAAI,EAAE;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,EAAE,EAAE,MAAM,CAAA;SAAE,CAAC;QACtC,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB;IAQD,iFAAiF;IACjF,WAAW,IAAI,gBAAgB;CAQhC;AAMD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,CAU7D;AAED,wBAAgB,YAAY,IAAI,YAAY,CAU3C;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAU/D;AAED,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,CAU7D;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAMzD"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/errors.ts — Error factory + domain error code constants.
|
|
3
|
+
*
|
|
4
|
+
* All MCP tool handlers in this package return either
|
|
5
|
+
* { ok: true, data, degraded? }
|
|
6
|
+
* or
|
|
7
|
+
* { ok: false, error: SkillFmToolError }
|
|
8
|
+
* (shape defined in the vendored contract `beacon-mcp-tools-v1.ts`).
|
|
9
|
+
*
|
|
10
|
+
* This file owns the canonical list of error codes. Codes are domain-prefixed
|
|
11
|
+
* `'SKILLFM.<DOMAIN>.<REASON>'` to make grep + log filtering reliable.
|
|
12
|
+
*
|
|
13
|
+
* TODO Week 5:
|
|
14
|
+
* - Wire codes into the brain-client retry classifier (retryable=true vs false)
|
|
15
|
+
* - Add i18n table for `userHint` (currently each handler ad-hoc fills en/zh)
|
|
16
|
+
* - Add a structured `cause` field (preserving original Error) once we have
|
|
17
|
+
* real network/io failures to surface — Phase 0 returns mocks, no real cause.
|
|
18
|
+
*/
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// Domain error code constants — keep alphabetized by domain
|
|
21
|
+
// ============================================================================
|
|
22
|
+
export const SKILLFM_ERROR_CODES = {
|
|
23
|
+
// AUTH — agent token / Pro tier / device binding
|
|
24
|
+
AUTH_NOT_BOUND: 'SKILLFM.AUTH.NOT_BOUND',
|
|
25
|
+
AUTH_INVALID_TOKEN: 'SKILLFM.AUTH.INVALID_TOKEN',
|
|
26
|
+
AUTH_PRO_REQUIRED: 'SKILLFM.AUTH.PRO_REQUIRED',
|
|
27
|
+
// NETWORK — brain endpoint reachability
|
|
28
|
+
NETWORK_UNREACHABLE: 'SKILLFM.NETWORK.UNREACHABLE',
|
|
29
|
+
NETWORK_TIMEOUT: 'SKILLFM.NETWORK.TIMEOUT',
|
|
30
|
+
// BRAIN — server-side responses (5xx, rate limit, contract drift)
|
|
31
|
+
BRAIN_RATE_LIMITED: 'SKILLFM.BRAIN.RATE_LIMITED',
|
|
32
|
+
BRAIN_INTERNAL: 'SKILLFM.BRAIN.INTERNAL',
|
|
33
|
+
BRAIN_CONTRACT_MISMATCH: 'SKILLFM.BRAIN.CONTRACT_MISMATCH',
|
|
34
|
+
// TOOL — local tool dispatch / NOT_IMPLEMENTED placeholders
|
|
35
|
+
TOOL_NOT_IMPLEMENTED: 'SKILLFM.TOOL.NOT_IMPLEMENTED',
|
|
36
|
+
TOOL_INVALID_INPUT: 'SKILLFM.TOOL.INVALID_INPUT',
|
|
37
|
+
// CACHE — local file persistence
|
|
38
|
+
CACHE_READ_FAILED: 'SKILLFM.CACHE.READ_FAILED',
|
|
39
|
+
CACHE_WRITE_FAILED: 'SKILLFM.CACHE.WRITE_FAILED',
|
|
40
|
+
// DECISION — client-side decision-engine rejections (frequency / pref / dedupe)
|
|
41
|
+
DECISION_FREQUENCY_LIMITED: 'SKILLFM.DECISION.FREQUENCY_LIMITED',
|
|
42
|
+
DECISION_USER_OPTED_OUT: 'SKILLFM.DECISION.USER_OPTED_OUT',
|
|
43
|
+
DECISION_DUPLICATE_SUGGESTION: 'SKILLFM.DECISION.DUPLICATE_SUGGESTION',
|
|
44
|
+
};
|
|
45
|
+
// ============================================================================
|
|
46
|
+
// SkillFmError — throwable Error subclass
|
|
47
|
+
// ============================================================================
|
|
48
|
+
/**
|
|
49
|
+
* Throwable error class. Tool handlers can `throw new SkillFmError(...)`
|
|
50
|
+
* and the MCP server boundary will convert into `SkillFmToolError` envelope.
|
|
51
|
+
*/
|
|
52
|
+
export class SkillFmError extends Error {
|
|
53
|
+
code;
|
|
54
|
+
userHint;
|
|
55
|
+
retryable;
|
|
56
|
+
constructor(opts) {
|
|
57
|
+
super(opts.message);
|
|
58
|
+
this.name = 'SkillFmError';
|
|
59
|
+
this.code = opts.code;
|
|
60
|
+
this.userHint = opts.userHint;
|
|
61
|
+
this.retryable = opts.retryable ?? false;
|
|
62
|
+
}
|
|
63
|
+
/** Convert to the wire-shape error returned inside `SkillFmToolResult.error`. */
|
|
64
|
+
toToolError() {
|
|
65
|
+
return {
|
|
66
|
+
code: this.code,
|
|
67
|
+
message: this.message,
|
|
68
|
+
userHint: this.userHint,
|
|
69
|
+
retryable: this.retryable,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// ============================================================================
|
|
74
|
+
// Factory helpers — common errors handlers throw
|
|
75
|
+
// ============================================================================
|
|
76
|
+
export function notImplemented(toolName) {
|
|
77
|
+
return new SkillFmError({
|
|
78
|
+
code: SKILLFM_ERROR_CODES.TOOL_NOT_IMPLEMENTED,
|
|
79
|
+
message: `Tool '${toolName}' is reserved in Phase 0 but not yet implemented.`,
|
|
80
|
+
userHint: {
|
|
81
|
+
en: `This SkillFM feature ships in Week 5 of Phase 1. Try one of the live tools instead.`,
|
|
82
|
+
zh: `这个 SkillFM 能力将在 Phase 1 第 5 周上线,先试其他已上线工具。`,
|
|
83
|
+
},
|
|
84
|
+
retryable: false,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
export function authNotBound() {
|
|
88
|
+
return new SkillFmError({
|
|
89
|
+
code: SKILLFM_ERROR_CODES.AUTH_NOT_BOUND,
|
|
90
|
+
message: 'No SKILLFM_AGENT_TOKEN configured. Run `skillfm-beacon-mcp bind` or set the env var.',
|
|
91
|
+
userHint: {
|
|
92
|
+
en: 'SkillFM is in degraded (offline) mode. Bind your agent token to enable cost-saving features.',
|
|
93
|
+
zh: 'SkillFM 当前是离线降级模式。绑定 agent token 以启用省钱功能。',
|
|
94
|
+
},
|
|
95
|
+
retryable: false,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
export function networkUnreachable(detail) {
|
|
99
|
+
return new SkillFmError({
|
|
100
|
+
code: SKILLFM_ERROR_CODES.NETWORK_UNREACHABLE,
|
|
101
|
+
message: `Cannot reach SkillFM brain: ${detail}`,
|
|
102
|
+
userHint: {
|
|
103
|
+
en: 'SkillFM cloud is unreachable. Falling back to cached rules.',
|
|
104
|
+
zh: 'SkillFM 云端不可达,回退到本地缓存规则。',
|
|
105
|
+
},
|
|
106
|
+
retryable: true,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
export function proRequired(featureName) {
|
|
110
|
+
return new SkillFmError({
|
|
111
|
+
code: SKILLFM_ERROR_CODES.AUTH_PRO_REQUIRED,
|
|
112
|
+
message: `Feature '${featureName}' requires SkillFM Pro.`,
|
|
113
|
+
userHint: {
|
|
114
|
+
en: `Upgrade to SkillFM Pro to unlock '${featureName}'.`,
|
|
115
|
+
zh: `升级 SkillFM Pro 解锁 '${featureName}'。`,
|
|
116
|
+
},
|
|
117
|
+
retryable: false,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
export function invalidInput(reason) {
|
|
121
|
+
return new SkillFmError({
|
|
122
|
+
code: SKILLFM_ERROR_CODES.TOOL_INVALID_INPUT,
|
|
123
|
+
message: `Invalid tool input: ${reason}`,
|
|
124
|
+
retryable: false,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/mcp/errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/mcp/index.ts — Beacon MCP subsystem entry. Boots a stdio MCP server.
|
|
3
|
+
*
|
|
4
|
+
* Originally `@skillfm/beacon-mcp` (PR #190), merged into `@skillfm/local` 2.7.0
|
|
5
|
+
* as a subcommand on Day 1 (2026-05-01). Invoked via:
|
|
6
|
+
*
|
|
7
|
+
* npx -y @skillfm/local mcp
|
|
8
|
+
*
|
|
9
|
+
* Lifecycle:
|
|
10
|
+
* 1. loadConfig() — env > config.json > degraded fallback
|
|
11
|
+
* 2. instantiate BrainClient + McpServer(name='skillfm-beacon', v=PKG_VERSION)
|
|
12
|
+
* 3. registerAllTools(server, deps) — 8 real + 25+ placeholder
|
|
13
|
+
* 4. install global error guards (stderr ONLY — stdout is MCP transport)
|
|
14
|
+
* 5. start StdioServerTransport
|
|
15
|
+
*
|
|
16
|
+
* BYOK red line: this file MUST NOT touch the user's LLM API keys, cookies,
|
|
17
|
+
* or vault secrets. The only credential read is SKILLFM_AGENT_TOKEN (an
|
|
18
|
+
* opaque token issued by SkillFM brain).
|
|
19
|
+
*
|
|
20
|
+
* MCP protocol invariant: stdout is reserved for JSON-RPC frames. Any debug
|
|
21
|
+
* output goes through `console.error` (stderr). Calling `console.log` from
|
|
22
|
+
* any imported module corrupts the transport — see exhaustive grep in CI.
|
|
23
|
+
*
|
|
24
|
+
* TODO Week 5:
|
|
25
|
+
* - HTTP transport mode (via env SKILLFM_BEACON_MCP_HTTP_PORT) — mirrors
|
|
26
|
+
* skillfm-local's dual transport setup for multi-tenant cloud agents.
|
|
27
|
+
* - Health-check endpoint exposed via local UDS for the Beacon GUI.
|
|
28
|
+
* - Self-upgrade probe (npm view + warn if a newer minor is out).
|
|
29
|
+
* - Structured request log → ~/.skillfm/beacon-mcp/server.log (rotating).
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* Boot the server. Awaits transport.start(); resolves when stdin closes.
|
|
33
|
+
*/
|
|
34
|
+
export declare function startServer(): Promise<void>;
|
|
35
|
+
export { BrainClient } from './brain-client.js';
|
|
36
|
+
export { loadConfig } from './config.js';
|
|
37
|
+
export { evaluateSuggestion } from './decision-engine.js';
|
|
38
|
+
export { loadPrefs, savePrefs, loadRules, saveRules, openSessionCache, BEACON_HOME, } from './cache.js';
|
|
39
|
+
export { SkillFmError, SKILLFM_ERROR_CODES } from './errors.js';
|
|
40
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAuCH;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAiCjD;AAKD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EACL,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,gBAAgB,EAChB,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/mcp/index.ts — Beacon MCP subsystem entry. Boots a stdio MCP server.
|
|
3
|
+
*
|
|
4
|
+
* Originally `@skillfm/beacon-mcp` (PR #190), merged into `@skillfm/local` 2.7.0
|
|
5
|
+
* as a subcommand on Day 1 (2026-05-01). Invoked via:
|
|
6
|
+
*
|
|
7
|
+
* npx -y @skillfm/local mcp
|
|
8
|
+
*
|
|
9
|
+
* Lifecycle:
|
|
10
|
+
* 1. loadConfig() — env > config.json > degraded fallback
|
|
11
|
+
* 2. instantiate BrainClient + McpServer(name='skillfm-beacon', v=PKG_VERSION)
|
|
12
|
+
* 3. registerAllTools(server, deps) — 8 real + 25+ placeholder
|
|
13
|
+
* 4. install global error guards (stderr ONLY — stdout is MCP transport)
|
|
14
|
+
* 5. start StdioServerTransport
|
|
15
|
+
*
|
|
16
|
+
* BYOK red line: this file MUST NOT touch the user's LLM API keys, cookies,
|
|
17
|
+
* or vault secrets. The only credential read is SKILLFM_AGENT_TOKEN (an
|
|
18
|
+
* opaque token issued by SkillFM brain).
|
|
19
|
+
*
|
|
20
|
+
* MCP protocol invariant: stdout is reserved for JSON-RPC frames. Any debug
|
|
21
|
+
* output goes through `console.error` (stderr). Calling `console.log` from
|
|
22
|
+
* any imported module corrupts the transport — see exhaustive grep in CI.
|
|
23
|
+
*
|
|
24
|
+
* TODO Week 5:
|
|
25
|
+
* - HTTP transport mode (via env SKILLFM_BEACON_MCP_HTTP_PORT) — mirrors
|
|
26
|
+
* skillfm-local's dual transport setup for multi-tenant cloud agents.
|
|
27
|
+
* - Health-check endpoint exposed via local UDS for the Beacon GUI.
|
|
28
|
+
* - Self-upgrade probe (npm view + warn if a newer minor is out).
|
|
29
|
+
* - Structured request log → ~/.skillfm/beacon-mcp/server.log (rotating).
|
|
30
|
+
*/
|
|
31
|
+
import { readFileSync } from 'node:fs';
|
|
32
|
+
import { dirname, join } from 'node:path';
|
|
33
|
+
import { fileURLToPath } from 'node:url';
|
|
34
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
35
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
36
|
+
import { loadConfig } from './config.js';
|
|
37
|
+
import { BrainClient } from './brain-client.js';
|
|
38
|
+
import { registerAllTools } from './tools/index.js';
|
|
39
|
+
const PACKAGE_NAME = '@skillfm/local';
|
|
40
|
+
// Resolve version from the host package.json so the MCP server reports the
|
|
41
|
+
// same version as `skillfm-local --version`. dist/mcp/index.js sits two
|
|
42
|
+
// levels deep relative to package.json.
|
|
43
|
+
const PACKAGE_VERSION = (() => {
|
|
44
|
+
try {
|
|
45
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
46
|
+
const pkg = JSON.parse(readFileSync(join(here, '..', '..', 'package.json'), 'utf-8'));
|
|
47
|
+
return typeof pkg.version === 'string' ? pkg.version : 'unknown';
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return 'unknown';
|
|
51
|
+
}
|
|
52
|
+
})();
|
|
53
|
+
/** Install process-wide guards so a bug in any handler can't tear down the
|
|
54
|
+
* MCP transport. All output → stderr. */
|
|
55
|
+
function installGlobalGuards() {
|
|
56
|
+
process.on('unhandledRejection', (reason) => {
|
|
57
|
+
console.error('[skillfm-local mcp] unhandledRejection:', reason);
|
|
58
|
+
});
|
|
59
|
+
process.on('uncaughtException', (err) => {
|
|
60
|
+
console.error('[skillfm-local mcp] uncaughtException:', err);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Boot the server. Awaits transport.start(); resolves when stdin closes.
|
|
65
|
+
*/
|
|
66
|
+
export async function startServer() {
|
|
67
|
+
installGlobalGuards();
|
|
68
|
+
const cfg = loadConfig();
|
|
69
|
+
if (cfg.degraded) {
|
|
70
|
+
console.error(`[skillfm-local mcp] WARNING: no SKILLFM_AGENT_TOKEN — running in degraded (offline) mode. ` +
|
|
71
|
+
`Brain-backed tools will return cached / mock data only.`);
|
|
72
|
+
}
|
|
73
|
+
const brain = new BrainClient({
|
|
74
|
+
agentToken: cfg.agentToken,
|
|
75
|
+
baseUrl: cfg.apiBaseUrl,
|
|
76
|
+
});
|
|
77
|
+
const server = new McpServer({
|
|
78
|
+
name: 'skillfm-beacon',
|
|
79
|
+
version: PACKAGE_VERSION,
|
|
80
|
+
});
|
|
81
|
+
registerAllTools(server, {
|
|
82
|
+
brain,
|
|
83
|
+
// Phase 0 stub: any token = Pro. Week 5 will read tier from token claims.
|
|
84
|
+
isProTier: !cfg.degraded,
|
|
85
|
+
});
|
|
86
|
+
const transport = new StdioServerTransport();
|
|
87
|
+
await server.connect(transport);
|
|
88
|
+
console.error(`[skillfm-local mcp] ${PACKAGE_NAME}@${PACKAGE_VERSION} ready on stdio (degraded=${cfg.degraded})`);
|
|
89
|
+
}
|
|
90
|
+
// Re-exports for downstream consumers that want to embed the server
|
|
91
|
+
// (e.g. Beacon GUI hosting the MCP server in-process). Surface kept
|
|
92
|
+
// intentionally narrow.
|
|
93
|
+
export { BrainClient } from './brain-client.js';
|
|
94
|
+
export { loadConfig } from './config.js';
|
|
95
|
+
export { evaluateSuggestion } from './decision-engine.js';
|
|
96
|
+
export { loadPrefs, savePrefs, loadRules, saveRules, openSessionCache, BEACON_HOME, } from './cache.js';
|
|
97
|
+
export { SkillFmError, SKILLFM_ERROR_CODES } from './errors.js';
|
|
98
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"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"}
|