@wootsup/mcp 0.1.0 → 0.3.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/CHANGELOG.md +148 -83
- package/README.md +31 -27
- package/SECURITY.md +15 -6
- package/dist/auth/keychain.d.ts +27 -1
- package/dist/auth/keychain.js +48 -2
- package/dist/auth/keychain.js.map +1 -1
- package/dist/cli-hint.d.ts +22 -0
- package/dist/cli-hint.js +55 -0
- package/dist/cli-hint.js.map +1 -0
- package/dist/index.js +97 -22
- package/dist/index.js.map +1 -1
- package/dist/install-skill.js +1 -1
- package/dist/modules/apimapper/cache.js +25 -17
- package/dist/modules/apimapper/cache.js.map +1 -1
- package/dist/modules/apimapper/client.d.ts +62 -1
- package/dist/modules/apimapper/client.js +555 -291
- package/dist/modules/apimapper/client.js.map +1 -1
- package/dist/modules/apimapper/connections.js +230 -75
- package/dist/modules/apimapper/connections.js.map +1 -1
- package/dist/modules/apimapper/credential-sanitizer.d.ts +5 -0
- package/dist/modules/apimapper/credential-sanitizer.js +60 -1
- package/dist/modules/apimapper/credential-sanitizer.js.map +1 -1
- package/dist/modules/apimapper/credentials.js +19 -47
- package/dist/modules/apimapper/credentials.js.map +1 -1
- package/dist/modules/apimapper/diagnose.js +21 -2
- package/dist/modules/apimapper/diagnose.js.map +1 -1
- package/dist/modules/apimapper/flows.js +60 -77
- package/dist/modules/apimapper/flows.js.map +1 -1
- package/dist/modules/apimapper/gateway/advanced-tool.js +56 -5
- package/dist/modules/apimapper/gateway/advanced-tool.js.map +1 -1
- package/dist/modules/apimapper/gateway/essentials.d.ts +1 -1
- package/dist/modules/apimapper/gateway/essentials.js +8 -1
- package/dist/modules/apimapper/gateway/essentials.js.map +1 -1
- package/dist/modules/apimapper/get-skill.d.ts +1 -1
- package/dist/modules/apimapper/get-skill.js +44 -6
- package/dist/modules/apimapper/get-skill.js.map +1 -1
- package/dist/modules/apimapper/graph.js +40 -36
- package/dist/modules/apimapper/graph.js.map +1 -1
- package/dist/modules/apimapper/index.js +2 -0
- package/dist/modules/apimapper/index.js.map +1 -1
- package/dist/modules/apimapper/library.js +425 -83
- package/dist/modules/apimapper/library.js.map +1 -1
- package/dist/modules/apimapper/license.js +12 -36
- package/dist/modules/apimapper/license.js.map +1 -1
- package/dist/modules/apimapper/local-sources.js +20 -34
- package/dist/modules/apimapper/local-sources.js.map +1 -1
- package/dist/modules/apimapper/misc.js +13 -27
- package/dist/modules/apimapper/misc.js.map +1 -1
- package/dist/modules/apimapper/onboarding.d.ts +30 -1
- package/dist/modules/apimapper/onboarding.js +114 -19
- package/dist/modules/apimapper/onboarding.js.map +1 -1
- package/dist/modules/apimapper/schema.js +9 -18
- package/dist/modules/apimapper/schema.js.map +1 -1
- package/dist/modules/apimapper/settings.js +49 -52
- package/dist/modules/apimapper/settings.js.map +1 -1
- package/dist/modules/apimapper/sites-tools.d.ts +29 -0
- package/dist/modules/apimapper/sites-tools.js +165 -0
- package/dist/modules/apimapper/sites-tools.js.map +1 -0
- package/dist/modules/apimapper/tool-result.d.ts +46 -0
- package/dist/modules/apimapper/tool-result.js +63 -0
- package/dist/modules/apimapper/tool-result.js.map +1 -0
- package/dist/modules/apimapper/toolslist-size.d.ts +11 -10
- package/dist/modules/apimapper/toolslist-size.js +16 -14
- package/dist/modules/apimapper/toolslist-size.js.map +1 -1
- package/dist/modules/apimapper/types.d.ts +21 -0
- package/dist/modules/apimapper/types.js.map +1 -1
- package/dist/modules/apimapper/whitelist-drift.d.ts +85 -0
- package/dist/modules/apimapper/whitelist-drift.js +360 -0
- package/dist/modules/apimapper/whitelist-drift.js.map +1 -0
- package/dist/modules/apimapper/workflows.js +82 -27
- package/dist/modules/apimapper/workflows.js.map +1 -1
- package/dist/modules/apimapper/yootheme-binding.d.ts +35 -0
- package/dist/modules/apimapper/yootheme-binding.js +186 -0
- package/dist/modules/apimapper/yootheme-binding.js.map +1 -0
- package/dist/platform/index.d.ts +56 -0
- package/dist/platform/index.js +151 -2
- package/dist/platform/index.js.map +1 -1
- package/dist/setup/detect-clients.d.ts +40 -1
- package/dist/setup/detect-clients.js +148 -1
- package/dist/setup/detect-clients.js.map +1 -1
- package/dist/setup/probe-handshake.js +40 -7
- package/dist/setup/probe-handshake.js.map +1 -1
- package/dist/setup/remove-config.d.ts +8 -0
- package/dist/setup/remove-config.js +145 -0
- package/dist/setup/remove-config.js.map +1 -0
- package/dist/setup/uninstall.d.ts +34 -0
- package/dist/setup/uninstall.js +147 -0
- package/dist/setup/uninstall.js.map +1 -0
- package/dist/setup-cli.d.ts +7 -0
- package/dist/setup-cli.js +29 -1
- package/dist/setup-cli.js.map +1 -1
- package/dist/sites/loader.d.ts +41 -0
- package/dist/sites/loader.js +119 -0
- package/dist/sites/loader.js.map +1 -0
- package/dist/sites/schema.d.ts +69 -0
- package/dist/sites/schema.js +71 -0
- package/dist/sites/schema.js.map +1 -0
- package/dist/sites/secret-resolver.d.ts +47 -0
- package/dist/sites/secret-resolver.js +150 -0
- package/dist/sites/secret-resolver.js.map +1 -0
- package/dist/skill-instructions.d.ts +1 -1
- package/dist/skill-instructions.js +5 -0
- package/dist/skill-instructions.js.map +1 -1
- package/dist/transports/stdio.js +4 -4
- package/dist/transports/stdio.js.map +1 -1
- package/dist/uninstall-skill.d.ts +27 -0
- package/dist/uninstall-skill.js +89 -0
- package/dist/uninstall-skill.js.map +1 -0
- package/docs/architecture.md +21 -21
- package/docs/customgraph-internal-migration.md +4 -4
- package/docs/security.md +2 -21
- package/docs/tools.md +40 -12
- package/manifest.json +77 -79
- package/package.json +68 -65
- package/skills/apimapper/SKILL.md +53 -7
- package/skills/apimapper/reference/conditional-style-multi-items.md +114 -0
- package/skills/apimapper/reference/jmespath-pitfalls.md +108 -0
- package/skills/apimapper/reference/joomla.md +1 -1
- package/skills/apimapper/reference/library-template-discovery.md +65 -0
- package/skills/apimapper/reference/merge-two-sources-on-key.md +99 -0
- package/skills/apimapper/reference/troubleshooting.md +20 -0
- package/skills/apimapper/reference/yootheme.md +1 -1
- package/dist/auth/oauth-provider.d.ts +0 -68
- package/dist/auth/oauth-provider.js +0 -232
- package/dist/auth/oauth-provider.js.map +0 -1
- package/dist/server-http.d.ts +0 -22
- package/dist/server-http.js +0 -159
- package/dist/server-http.js.map +0 -1
- package/dist/transports/http.d.ts +0 -29
- package/dist/transports/http.js +0 -267
- package/dist/transports/http.js.map +0 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type SiteEntryT } from "./schema.js";
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the `op` binary to an absolute path so it works under a stripped-down
|
|
4
|
+
* GUI PATH. Precedence: explicit `APIMAPPER_OP_BINARY` env override → first
|
|
5
|
+
* existing well-known absolute path → bare `op` (PATH).
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveOpBinary(env?: NodeJS.ProcessEnv): string;
|
|
8
|
+
/** Resolved bearer + its provenance for downstream logs (never the token). */
|
|
9
|
+
export interface ResolvedBearer {
|
|
10
|
+
readonly token: string;
|
|
11
|
+
readonly source: "plain" | "op";
|
|
12
|
+
}
|
|
13
|
+
/** Domain-tagged error so callers can branch on `code`. */
|
|
14
|
+
export declare class SecretResolverError extends Error {
|
|
15
|
+
readonly code: string;
|
|
16
|
+
constructor(code: string, message: string);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Signature of `node:child_process.execFile` reduced to the callback we rely
|
|
20
|
+
* on. Injecting this lets tests provide a deterministic substitute without a
|
|
21
|
+
* `vi.mock('node:child_process')` side-effect.
|
|
22
|
+
*/
|
|
23
|
+
export type ExecFileLike = (file: string, args: readonly string[], options: {
|
|
24
|
+
timeout?: number;
|
|
25
|
+
maxBuffer?: number;
|
|
26
|
+
}, callback: (error: (Error & {
|
|
27
|
+
code?: string | number;
|
|
28
|
+
signal?: string;
|
|
29
|
+
}) | null, stdout: string, stderr: string) => void) => void;
|
|
30
|
+
export interface ResolveSiteBearerOptions {
|
|
31
|
+
/** Injected execFile for tests. Defaults to Node's real execFile. */
|
|
32
|
+
execFile?: ExecFileLike;
|
|
33
|
+
/** Resolved `op` binary path. Defaults to {@link resolveOpBinary}. */
|
|
34
|
+
opBinary?: string;
|
|
35
|
+
/** Per-call timeout override. Defaults to {@link OP_TIMEOUT_MS}. */
|
|
36
|
+
timeoutMs?: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Resolve the bearer token for a site entry.
|
|
40
|
+
*
|
|
41
|
+
* - `bearer` present → returned verbatim (`source:'plain'`).
|
|
42
|
+
* - `bearer_ref` set → `op read <ref>` via execFile (`source:'op'`).
|
|
43
|
+
* - neither → throws SecretResolverError('BEARER_MISSING').
|
|
44
|
+
*
|
|
45
|
+
* Never logs the token; never mutates the input site.
|
|
46
|
+
*/
|
|
47
|
+
export declare function resolveSiteBearer(site: SiteEntryT, opts?: ResolveSiteBearerOptions): Promise<ResolvedBearer>;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// src/sites/secret-resolver.ts — Phase 3.
|
|
2
|
+
//
|
|
3
|
+
// Produce a bearer token for a SiteEntryT at call time, supporting either a
|
|
4
|
+
// plain in-file token (`bearer`) or a 1Password Secret Reference (`bearer_ref`)
|
|
5
|
+
// resolved through the `op` CLI. Mirrors the YT Builder defence model
|
|
6
|
+
// (yt-builder-mcp/packages/mcp/src/sites/secret-resolver.ts), kept simple:
|
|
7
|
+
//
|
|
8
|
+
// - `execFile`, NEVER `exec`/`spawn` with `shell:true`. `op read <ref>` runs
|
|
9
|
+
// as a direct argv invocation — no shell, no metacharacter expansion. The
|
|
10
|
+
// schema enforces OP_REF_REGEX on load; we re-enforce it here as
|
|
11
|
+
// defence-in-depth (a hand-edited sites.json could otherwise sneak a
|
|
12
|
+
// payload past validation).
|
|
13
|
+
// - Hard timeout so a biometric-stalled `op` can't wedge the agent loop.
|
|
14
|
+
// - Pluggable `execFile` for testing — no global child_process monkey-patch.
|
|
15
|
+
import { execFile as nodeExecFile } from "node:child_process";
|
|
16
|
+
import { existsSync } from "node:fs";
|
|
17
|
+
import { OP_REF_REGEX } from "./schema.js";
|
|
18
|
+
/** Hard cap for any single `op read` invocation. */
|
|
19
|
+
const OP_TIMEOUT_MS = 5_000;
|
|
20
|
+
/**
|
|
21
|
+
* Common absolute install locations for the 1Password CLI. GUI-spawned MCP
|
|
22
|
+
* hosts (Claude Desktop, etc.) launch the server subprocess with a minimal
|
|
23
|
+
* PATH that usually omits `/opt/homebrew/bin` and `/usr/local/bin`, so a bare
|
|
24
|
+
* `op` resolves to ENOENT even when `op` works in the user's shell. Probe these
|
|
25
|
+
* absolute paths before falling back to PATH.
|
|
26
|
+
*/
|
|
27
|
+
const OP_BINARY_CANDIDATES = [
|
|
28
|
+
"/opt/homebrew/bin/op",
|
|
29
|
+
"/usr/local/bin/op",
|
|
30
|
+
"/usr/bin/op",
|
|
31
|
+
];
|
|
32
|
+
/**
|
|
33
|
+
* Resolve the `op` binary to an absolute path so it works under a stripped-down
|
|
34
|
+
* GUI PATH. Precedence: explicit `APIMAPPER_OP_BINARY` env override → first
|
|
35
|
+
* existing well-known absolute path → bare `op` (PATH).
|
|
36
|
+
*/
|
|
37
|
+
export function resolveOpBinary(env = process.env) {
|
|
38
|
+
const override = (env.APIMAPPER_OP_BINARY ?? "").trim();
|
|
39
|
+
if (override.length > 0)
|
|
40
|
+
return override;
|
|
41
|
+
for (const candidate of OP_BINARY_CANDIDATES) {
|
|
42
|
+
try {
|
|
43
|
+
if (existsSync(candidate))
|
|
44
|
+
return candidate;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// ignore stat errors and keep probing
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return "op";
|
|
51
|
+
}
|
|
52
|
+
/** Domain-tagged error so callers can branch on `code`. */
|
|
53
|
+
export class SecretResolverError extends Error {
|
|
54
|
+
code;
|
|
55
|
+
constructor(code, message) {
|
|
56
|
+
super(message);
|
|
57
|
+
this.code = code;
|
|
58
|
+
this.name = "SecretResolverError";
|
|
59
|
+
Object.setPrototypeOf(this, SecretResolverError.prototype);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** Default adapter wrapping Node's real `execFile` with utf8 encoding. */
|
|
63
|
+
const defaultExecFile = (file, args, options, callback) => {
|
|
64
|
+
nodeExecFile(file, args, { ...options, encoding: "utf8" }, (err, stdout, stderr) => {
|
|
65
|
+
callback(err, stdout, stderr);
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
const OP_EXIT_CODE_HINTS = {
|
|
69
|
+
6: "Not signed in to 1Password CLI. Run `op signin` and retry.",
|
|
70
|
+
1: "Generic op CLI error — verify the secret reference exists and the vault is accessible.",
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Resolve the bearer token for a site entry.
|
|
74
|
+
*
|
|
75
|
+
* - `bearer` present → returned verbatim (`source:'plain'`).
|
|
76
|
+
* - `bearer_ref` set → `op read <ref>` via execFile (`source:'op'`).
|
|
77
|
+
* - neither → throws SecretResolverError('BEARER_MISSING').
|
|
78
|
+
*
|
|
79
|
+
* Never logs the token; never mutates the input site.
|
|
80
|
+
*/
|
|
81
|
+
export async function resolveSiteBearer(site, opts = {}) {
|
|
82
|
+
if (site.bearer !== undefined && site.bearer.length > 0) {
|
|
83
|
+
return { token: site.bearer, source: "plain" };
|
|
84
|
+
}
|
|
85
|
+
const ref = site.bearer_ref;
|
|
86
|
+
if (ref === undefined || ref.length === 0) {
|
|
87
|
+
throw new SecretResolverError("BEARER_MISSING", `Site '${site.site_id}' has neither an inline 'bearer' nor a 'bearer_ref'.`);
|
|
88
|
+
}
|
|
89
|
+
// Defence-in-depth: re-validate the ref shape BEFORE it reaches a subprocess
|
|
90
|
+
// argv. The schema enforces this on load; a hand-edited sites.json could
|
|
91
|
+
// bypass that.
|
|
92
|
+
if (!OP_REF_REGEX.test(ref)) {
|
|
93
|
+
throw new SecretResolverError("OP_REF_INVALID", `Site '${site.site_id}' has an invalid 'bearer_ref' shape — must match op://<vault>/<item>/<field>.`);
|
|
94
|
+
}
|
|
95
|
+
const execFile = opts.execFile ?? defaultExecFile;
|
|
96
|
+
const opBinary = opts.opBinary ?? resolveOpBinary();
|
|
97
|
+
const timeoutMs = opts.timeoutMs ?? OP_TIMEOUT_MS;
|
|
98
|
+
const stdout = await runOpRead(execFile, opBinary, ref, timeoutMs, site.site_id);
|
|
99
|
+
const token = stdout.trim();
|
|
100
|
+
if (token.length === 0) {
|
|
101
|
+
throw new SecretResolverError("OP_EMPTY_OUTPUT", `op returned empty output for '${ref}' — verify the field exists and is non-empty.`);
|
|
102
|
+
}
|
|
103
|
+
return { token, source: "op" };
|
|
104
|
+
}
|
|
105
|
+
/** Invoke `op read <ref>` with a hard timeout that survives a stalled child. */
|
|
106
|
+
function runOpRead(execFile, opBinary, ref, timeoutMs, siteId) {
|
|
107
|
+
return new Promise((resolve, reject) => {
|
|
108
|
+
let settled = false;
|
|
109
|
+
const timer = setTimeout(() => {
|
|
110
|
+
if (settled)
|
|
111
|
+
return;
|
|
112
|
+
settled = true;
|
|
113
|
+
reject(new SecretResolverError("OP_TIMEOUT", `op read '${ref}' exceeded ${timeoutMs}ms — is 1Password CLI hung on a biometric prompt?`));
|
|
114
|
+
}, timeoutMs);
|
|
115
|
+
if (typeof timer.unref === "function")
|
|
116
|
+
timer.unref();
|
|
117
|
+
execFile(opBinary, ["read", ref], { timeout: timeoutMs, maxBuffer: 1024 * 64 }, (err, stdout, stderr) => {
|
|
118
|
+
if (settled)
|
|
119
|
+
return;
|
|
120
|
+
settled = true;
|
|
121
|
+
clearTimeout(timer);
|
|
122
|
+
if (err) {
|
|
123
|
+
reject(mapExecError(err, stderr, ref, siteId));
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
resolve(stdout);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
function mapExecError(err, stderr, ref, siteId) {
|
|
131
|
+
const codeStr = typeof err.code === "string" ? err.code : "";
|
|
132
|
+
const codeNum = typeof err.code === "number" ? err.code : NaN;
|
|
133
|
+
if (codeStr === "ENOENT") {
|
|
134
|
+
return new SecretResolverError("OP_CLI_MISSING", `op CLI not found for site '${siteId}' — install 1Password CLI (or set APIMAPPER_OP_BINARY), ` +
|
|
135
|
+
"or use an inline 'bearer' field instead of 'bearer_ref'.");
|
|
136
|
+
}
|
|
137
|
+
const stderrLower = (stderr || "").toLowerCase();
|
|
138
|
+
if (stderrLower.includes("isn't an item") || stderrLower.includes("item not found")) {
|
|
139
|
+
return new SecretResolverError("OP_ITEM_NOT_FOUND", `1Password item not found at '${ref}'. Verify the ref points to an existing item.`);
|
|
140
|
+
}
|
|
141
|
+
if (Number.isFinite(codeNum)) {
|
|
142
|
+
const hint = OP_EXIT_CODE_HINTS[codeNum];
|
|
143
|
+
if (codeNum === 6) {
|
|
144
|
+
return new SecretResolverError("OP_NOT_SIGNED_IN", hint ?? "op exited 6 — not signed in.");
|
|
145
|
+
}
|
|
146
|
+
return new SecretResolverError("OP_EXEC_FAILED", `op read '${ref}' failed with exit ${codeNum}: ${(stderr || "").trim() || (hint ?? "no stderr")}`);
|
|
147
|
+
}
|
|
148
|
+
return new SecretResolverError("OP_EXEC_FAILED", `op read '${ref}' failed: ${err.message}`);
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=secret-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret-resolver.js","sourceRoot":"","sources":["../../src/sites/secret-resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,7 +8,7 @@ export declare const FALLBACK_INSTRUCTIONS = "Use this MCP to manage API Mapper
|
|
|
8
8
|
*
|
|
9
9
|
* Topics MUST stay in sync with SKILL_TOPICS in modules/apimapper/get-skill.ts.
|
|
10
10
|
*/
|
|
11
|
-
export declare const SKILL_TOPIC_POINTER = "\n\n## Available skill topics\n\nFor deeper guidance on specific aspects of API Mapper, call:\n apimapper_get_skill({ topic })\n\nAvailable topics:\n- getting-started \u2014 Quickstart, common tools, common pitfalls\n- oauth \u2014 Wiring OAuth-protected sources (Google, Pexels, Instagram, Notion, ...)\n- yootheme \u2014 Publish flows as YOOtheme sources; Save-vs-Publish\n- joomla \u2014 Joomla 3-tier auth, com_ajax envelope, system plugin layout\n- troubleshooting \u2014 HTTP status triage, flow issues, credential states\n";
|
|
11
|
+
export declare const SKILL_TOPIC_POINTER = "\n\n## Available skill topics\n\nFor deeper guidance on specific aspects of API Mapper, call:\n apimapper_get_skill({ topic })\n\nAvailable topics:\n- getting-started \u2014 Quickstart, common tools, common pitfalls\n- oauth \u2014 Wiring OAuth-protected sources (Google, Pexels, Instagram, Notion, ...)\n- yootheme \u2014 Publish flows as YOOtheme sources; Save-vs-Publish\n- joomla \u2014 Joomla 3-tier auth, com_ajax envelope, system plugin layout\n- troubleshooting \u2014 HTTP status triage, flow issues, credential states\n- render \u2014 Render a flow as a diagram (viz-type, flow-diagram output)\n- jmespath-pitfalls \u2014 The 9 traps + date-primitive functions (date_weekday, date_iso_to_time, date_iso_to_date, time_in_window) + depth-cap warning\n- merge-two-sources-on-key \u2014 Joining two sources on a shared key, including date-primitive bridging for weekday-name \u2194 ISO-datetime joins\n- conditional-style-multi-items \u2014 Per-row YOOtheme styles driven by a row field (text_color, panel_style)\n- library-template-discovery \u2014 Inspect a library template's contract before activating\n";
|
|
12
12
|
/**
|
|
13
13
|
* Loads MCP server instructions from skills/apimapper/SKILL.md.
|
|
14
14
|
*
|
|
@@ -30,6 +30,11 @@ Available topics:
|
|
|
30
30
|
- yootheme — Publish flows as YOOtheme sources; Save-vs-Publish
|
|
31
31
|
- joomla — Joomla 3-tier auth, com_ajax envelope, system plugin layout
|
|
32
32
|
- troubleshooting — HTTP status triage, flow issues, credential states
|
|
33
|
+
- render — Render a flow as a diagram (viz-type, flow-diagram output)
|
|
34
|
+
- jmespath-pitfalls — The 9 traps + date-primitive functions (date_weekday, date_iso_to_time, date_iso_to_date, time_in_window) + depth-cap warning
|
|
35
|
+
- merge-two-sources-on-key — Joining two sources on a shared key, including date-primitive bridging for weekday-name ↔ ISO-datetime joins
|
|
36
|
+
- conditional-style-multi-items — Per-row YOOtheme styles driven by a row field (text_color, panel_style)
|
|
37
|
+
- library-template-discovery — Inspect a library template's contract before activating
|
|
33
38
|
`;
|
|
34
39
|
/**
|
|
35
40
|
* Loads MCP server instructions from skills/apimapper/SKILL.md.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skill-instructions.js","sourceRoot":"","sources":["../src/skill-instructions.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,0EAA0E;AAC1E,yCAAyC;AACzC,EAAE;AACF,yEAAyE;AAEzE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnD,MAAM,CAAC,MAAM,qBAAqB,GAChC,8FAA8F,CAAC;AAEjG,MAAM,UAAU,GAAG,GAAG,CAAC;AAEvB;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG
|
|
1
|
+
{"version":3,"file":"skill-instructions.js","sourceRoot":"","sources":["../src/skill-instructions.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,0EAA0E;AAC1E,yCAAyC;AACzC,EAAE;AACF,yEAAyE;AAEzE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnD,MAAM,CAAC,MAAM,qBAAqB,GAChC,8FAA8F,CAAC;AAEjG,MAAM,UAAU,GAAG,GAAG,CAAC;AAEvB;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;CAkBlC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,MAAM,OAAO,GAAG,OAAO,IAAI,WAAW,EAAE,CAAC;IACzC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC;IAEnE,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3B,OAAO,qBAAqB,GAAG,mBAAmB,CAAC;IACrD,CAAC;IAED,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,qBAAqB,GAAG,mBAAmB,CAAC;IACrD,CAAC;IAED,MAAM,IAAI,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,qBAAqB,GAAG,mBAAmB,CAAC;IACrD,CAAC;IACD,yEAAyE;IACzE,qEAAqE;IACrE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,GAAG,mBAAmB,CAAC;AACzD,CAAC;AAED;;;;;GAKG;AACH,SAAS,gBAAgB,CAAC,KAAa;IACrC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,iDAAiD;IACjD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC1C,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAChB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,kEAAkE;IAClE,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC;IAC/C,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACd,qDAAqD;QACrD,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,WAAW;IAClB,8DAA8D;IAC9D,qEAAqE;IACrE,wCAAwC;IACxC,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACrD,OAAO,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAC7B,CAAC"}
|
package/dist/transports/stdio.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
// src/transports/stdio.ts
|
|
1
|
+
// src/transports/stdio.ts
|
|
2
2
|
//
|
|
3
3
|
// Thin factory that connects an McpServer to a StdioServerTransport so the
|
|
4
|
-
// process speaks JSON-RPC over stdin/stdout.
|
|
5
|
-
//
|
|
6
|
-
//
|
|
4
|
+
// process speaks JSON-RPC over stdin/stdout. stdio is the only transport the
|
|
5
|
+
// MCP server ships — it runs over stdio for both the npm/npx path and the
|
|
6
|
+
// Claude Desktop DXT bundle.
|
|
7
7
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
8
|
/**
|
|
9
9
|
* Connect the given McpServer to a fresh `StdioServerTransport`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/transports/stdio.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/transports/stdio.ts"],"names":[],"mappings":"AAAA,0BAA0B;AAC1B,EAAE;AACF,2EAA2E;AAC3E,6EAA6E;AAC7E,0EAA0E;AAC1E,6BAA6B;AAG7B,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAiB;IAClD,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface UninstallSkillOptions {
|
|
2
|
+
/** Skills dir holding the `apimapper/` folder. Default: ~/.claude/skills/ */
|
|
3
|
+
targetSkillsDir?: string;
|
|
4
|
+
/** AGENTS.md file the marker was appended to. Default: ~/AGENTS.md */
|
|
5
|
+
targetAgentsFile?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface UninstallSkillResult {
|
|
8
|
+
skillRemoved: boolean;
|
|
9
|
+
markerRemoved: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Strip the install-skill marker block from AGENTS.md content.
|
|
13
|
+
*
|
|
14
|
+
* install-skill appends a block of the shape:
|
|
15
|
+
*
|
|
16
|
+
* \n\n## API Mapper MCP\n\n<MARKER_LINE>\n<one descriptive line>\n
|
|
17
|
+
*
|
|
18
|
+
* We remove the `## API Mapper MCP` heading (and any leading blank lines
|
|
19
|
+
* before it) through the descriptive line that follows the marker. The match
|
|
20
|
+
* is anchored on MARKER_LINE so an edited heading still gets cleaned as long
|
|
21
|
+
* as the marker is intact. Returns the cleaned text, or `null` if no marker
|
|
22
|
+
* was present (caller treats that as "nothing removed").
|
|
23
|
+
*/
|
|
24
|
+
export declare function stripMarkerBlock(content: string): string | null;
|
|
25
|
+
export declare function uninstallSkill(options?: UninstallSkillOptions): Promise<UninstallSkillResult>;
|
|
26
|
+
/** True iff the skill dir is present (drives the default skill-removal prompt). */
|
|
27
|
+
export declare function isSkillInstalled(targetSkillsDir?: string): boolean;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// src/uninstall-skill.ts — reverse of install-skill.ts.
|
|
2
|
+
//
|
|
3
|
+
// uninstallSkill() removes the copied skills/apimapper/ directory and strips
|
|
4
|
+
// the marker block that install-skill appended to AGENTS.md. Idempotent: a
|
|
5
|
+
// missing skill dir or absent marker is a clean no-op. Preserves every other
|
|
6
|
+
// skill + all other AGENTS.md content.
|
|
7
|
+
import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
import { MARKER_LINE } from "./install-skill.js";
|
|
11
|
+
/**
|
|
12
|
+
* Strip the install-skill marker block from AGENTS.md content.
|
|
13
|
+
*
|
|
14
|
+
* install-skill appends a block of the shape:
|
|
15
|
+
*
|
|
16
|
+
* \n\n## API Mapper MCP\n\n<MARKER_LINE>\n<one descriptive line>\n
|
|
17
|
+
*
|
|
18
|
+
* We remove the `## API Mapper MCP` heading (and any leading blank lines
|
|
19
|
+
* before it) through the descriptive line that follows the marker. The match
|
|
20
|
+
* is anchored on MARKER_LINE so an edited heading still gets cleaned as long
|
|
21
|
+
* as the marker is intact. Returns the cleaned text, or `null` if no marker
|
|
22
|
+
* was present (caller treats that as "nothing removed").
|
|
23
|
+
*/
|
|
24
|
+
export function stripMarkerBlock(content) {
|
|
25
|
+
if (!content.includes(MARKER_LINE))
|
|
26
|
+
return null;
|
|
27
|
+
const lines = content.split("\n");
|
|
28
|
+
const markerIdx = lines.findIndex((l) => l.trim() === MARKER_LINE.trim());
|
|
29
|
+
if (markerIdx === -1) {
|
|
30
|
+
// Marker present inline but not on its own line — fall back to a plain
|
|
31
|
+
// substring strip of the marker line itself.
|
|
32
|
+
return content.split(MARKER_LINE).join("");
|
|
33
|
+
}
|
|
34
|
+
// Walk backwards to include the `## API Mapper MCP` heading and the blank
|
|
35
|
+
// lines that separated it from the previous content.
|
|
36
|
+
let start = markerIdx;
|
|
37
|
+
// Skip a blank line directly above the marker (between heading and marker).
|
|
38
|
+
if (start > 0 && lines[start - 1].trim() === "")
|
|
39
|
+
start -= 1;
|
|
40
|
+
// Include the heading line if it is the install-skill heading.
|
|
41
|
+
if (start > 0 && lines[start - 1].trim() === "## API Mapper MCP")
|
|
42
|
+
start -= 1;
|
|
43
|
+
// Swallow leading blank separator lines above the heading.
|
|
44
|
+
while (start > 0 && lines[start - 1].trim() === "")
|
|
45
|
+
start -= 1;
|
|
46
|
+
// Walk forward to include the descriptive line(s) after the marker, up to
|
|
47
|
+
// the next blank line or EOF.
|
|
48
|
+
let end = markerIdx + 1;
|
|
49
|
+
while (end < lines.length && lines[end].trim() !== "")
|
|
50
|
+
end += 1;
|
|
51
|
+
const head = lines.slice(0, start);
|
|
52
|
+
const tail = lines.slice(end);
|
|
53
|
+
let next = [...head, ...tail].join("\n");
|
|
54
|
+
// Normalise: collapse a triple+ newline seam left by the excision and keep
|
|
55
|
+
// a single trailing newline if the original had one.
|
|
56
|
+
next = next.replace(/\n{3,}/g, "\n\n");
|
|
57
|
+
if (content.endsWith("\n") && next.length > 0 && !next.endsWith("\n")) {
|
|
58
|
+
next += "\n";
|
|
59
|
+
}
|
|
60
|
+
return next;
|
|
61
|
+
}
|
|
62
|
+
export async function uninstallSkill(options = {}) {
|
|
63
|
+
const targetSkillsDir = options.targetSkillsDir ?? join(homedir(), ".claude", "skills");
|
|
64
|
+
const targetAgentsFile = options.targetAgentsFile ?? join(homedir(), "AGENTS.md");
|
|
65
|
+
// 1. Remove the skill dir.
|
|
66
|
+
let skillRemoved = false;
|
|
67
|
+
const skillDir = join(targetSkillsDir, "apimapper");
|
|
68
|
+
if (existsSync(skillDir)) {
|
|
69
|
+
rmSync(skillDir, { recursive: true, force: true });
|
|
70
|
+
skillRemoved = true;
|
|
71
|
+
}
|
|
72
|
+
// 2. Strip the marker block from AGENTS.md (do NOT create it if absent).
|
|
73
|
+
let markerRemoved = false;
|
|
74
|
+
if (existsSync(targetAgentsFile)) {
|
|
75
|
+
const existing = readFileSync(targetAgentsFile, "utf8");
|
|
76
|
+
const stripped = stripMarkerBlock(existing);
|
|
77
|
+
if (stripped !== null) {
|
|
78
|
+
writeFileSync(targetAgentsFile, stripped, "utf8");
|
|
79
|
+
markerRemoved = true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return { skillRemoved, markerRemoved };
|
|
83
|
+
}
|
|
84
|
+
/** True iff the skill dir is present (drives the default skill-removal prompt). */
|
|
85
|
+
export function isSkillInstalled(targetSkillsDir) {
|
|
86
|
+
const dir = targetSkillsDir ?? join(homedir(), ".claude", "skills");
|
|
87
|
+
return existsSync(join(dir, "apimapper"));
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=uninstall-skill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uninstall-skill.js","sourceRoot":"","sources":["../src/uninstall-skill.ts"],"names":[],"mappings":"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"}
|
package/docs/architecture.md
CHANGED
|
@@ -11,17 +11,17 @@ internal plan-doc `~/Projekte/getimo/40-Plans/active/
|
|
|
11
11
|
┌──────────────────────────────────────────────────────────────┐
|
|
12
12
|
│ AI client │
|
|
13
13
|
│ (Claude Desktop · Claude Code · Cursor · VS Code · Cline · Codex CLI) │
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
└─────────────────────────────┬────────────────────────────────┘
|
|
15
|
+
│ stdio
|
|
16
|
+
│ (npx / DXT)
|
|
17
|
+
▼
|
|
18
18
|
┌──────────────────────────────────────────────────────────────┐
|
|
19
19
|
│ @wootsup/mcp (this package) │
|
|
20
20
|
│ │
|
|
21
21
|
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
|
|
22
22
|
│ │ Transport │→ │ Tool │→ │ Platform router │ │
|
|
23
|
-
│ │ stdio
|
|
24
|
-
│ │
|
|
23
|
+
│ │ stdio │ │ registry │ │ (WP / Joomla) │ │
|
|
24
|
+
│ │ │ │ (79 tools) │ │ │ │
|
|
25
25
|
│ └──────────────┘ └──────────────┘ └──────────────────┘ │
|
|
26
26
|
│ │ ▲ │ │
|
|
27
27
|
│ │ │ │ │
|
|
@@ -54,11 +54,9 @@ internal plan-doc `~/Projekte/getimo/40-Plans/active/
|
|
|
54
54
|
## Five components
|
|
55
55
|
|
|
56
56
|
1. **Transport layer** (`src/transports/`)
|
|
57
|
-
- `stdio.ts` —
|
|
58
|
-
by the AI client.
|
|
59
|
-
|
|
60
|
-
OAuth 2.0 PKCE-required authorization server. Bearer tokens are
|
|
61
|
-
site-bound; cross-origin requests are rejected.
|
|
57
|
+
- `stdio.ts` — the only transport. Pipes JSON-RPC over stdin/stdout.
|
|
58
|
+
Spawned by the AI client (npx / DXT). The server opens no network
|
|
59
|
+
listener.
|
|
62
60
|
|
|
63
61
|
2. **Tool registry** (`src/modules/apimapper/`)
|
|
64
62
|
- 15 register-files (connections, credentials, flows, graph,
|
|
@@ -86,7 +84,7 @@ internal plan-doc `~/Projekte/getimo/40-Plans/active/
|
|
|
86
84
|
5. **Setup + skills** (`src/setup-cli.ts`, `skills/apimapper/`)
|
|
87
85
|
- `@clack/prompts` interactive wizard.
|
|
88
86
|
- Auto-detects AI clients, patches their MCP config idempotently.
|
|
89
|
-
- Ships the skill files (`SKILL.md` +
|
|
87
|
+
- Ships the skill files (`SKILL.md` + 9 reference docs) into
|
|
90
88
|
`~/.claude/skills/apimapper/`.
|
|
91
89
|
|
|
92
90
|
## Auth flow (token issuance)
|
|
@@ -105,17 +103,19 @@ internal plan-doc `~/Projekte/getimo/40-Plans/active/
|
|
|
105
103
|
At runtime, the server reads the token from the keychain on every
|
|
106
104
|
launch — tokens never live in the client config file.
|
|
107
105
|
|
|
108
|
-
##
|
|
106
|
+
## Transport rationale
|
|
109
107
|
|
|
110
|
-
|
|
111
|
-
|---|---|---|
|
|
112
|
-
| Where it runs | User's machine | User's machine or remote |
|
|
113
|
-
| Auth | Bearer from keychain | OAuth 2.0 PKCE bound to site |
|
|
114
|
-
| Use case | Local dev, single user | Remote AI agents (future) |
|
|
115
|
-
| Discovery | Static (one-tool-set) | Dynamic per session |
|
|
108
|
+
The MCP server ships **stdio-only**:
|
|
116
109
|
|
|
117
|
-
|
|
118
|
-
|
|
110
|
+
| | stdio |
|
|
111
|
+
|---|---|
|
|
112
|
+
| Where it runs | User's machine |
|
|
113
|
+
| Auth | Bearer (`amk_*`) from keychain / profile |
|
|
114
|
+
| Use case | Local dev, single user |
|
|
115
|
+
| Discovery | Static (one tool-set) |
|
|
116
|
+
|
|
117
|
+
The AI client spawns the server as a child process and speaks JSON-RPC
|
|
118
|
+
over stdin/stdout; there is no HTTP, SSE, or remote transport.
|
|
119
119
|
|
|
120
120
|
## Multi-platform rationale
|
|
121
121
|
|
|
@@ -99,10 +99,10 @@ then delete.
|
|
|
99
99
|
|
|
100
100
|
```bash
|
|
101
101
|
npm view @wootsup/mcp version
|
|
102
|
-
# Expect: 0.
|
|
102
|
+
# Expect: 0.2.0 (rc.X-wN.M tail retired — plain semver now)
|
|
103
103
|
|
|
104
104
|
npm view @wootsup/mcp dist-tags
|
|
105
|
-
# Expect:
|
|
105
|
+
# Expect: latest now carries 0.2.0 (rc dist-tag track retired)
|
|
106
106
|
```
|
|
107
107
|
|
|
108
108
|
3. **Run the setup wizard with the published package**
|
|
@@ -154,8 +154,8 @@ then delete.
|
|
|
154
154
|
status: ok
|
|
155
155
|
platform: wordpress (or joomla)
|
|
156
156
|
site_url: https://dev.wootsup.com/wordpress
|
|
157
|
-
plugin_version: 2.0.
|
|
158
|
-
tool_count:
|
|
157
|
+
plugin_version: 2.0.13
|
|
158
|
+
tool_count: 79
|
|
159
159
|
```
|
|
160
160
|
|
|
161
161
|
8. **Run the full smoke suite**
|
package/docs/security.md
CHANGED
|
@@ -16,12 +16,12 @@ reporting process and supported-version policy, see
|
|
|
16
16
|
| T6 | Confused-deputy LLM (read-only token gets `*_delete` tools listed) | High | Med | Scope filter excludes destructive tools from tool-list when the token lacks the scope. |
|
|
17
17
|
| T7 | Local-fs token theft (no keychain) | Med | Med | `0600` file mode; keychain default on macOS/Windows; SECURITY.md warns about multi-user environments. |
|
|
18
18
|
| T8 | DXT supply-chain (malicious .dxt) | Low | Critical | DXT bundle uploaded as a signed GitHub Release asset; `apimapper-mcp-publish.yml` workflow publishes with `--provenance`. |
|
|
19
|
-
| T9 |
|
|
19
|
+
| T9 | Network exposure of the MCP server | n/a | n/a | Not applicable — the server is stdio-only and opens no network listener. |
|
|
20
20
|
| T10 | TOCTOU on rotate (old token works after rotate UI shows success) | Low | Med | Rotation is atomic on the server; the response shows the new token only after the old one is invalidated. |
|
|
21
21
|
|
|
22
22
|
## Token rotation
|
|
23
23
|
|
|
24
|
-
- **From admin UI:** API Mapper →
|
|
24
|
+
- **From admin UI:** API Mapper → **⋮** menu → Settings → MCP Access → **Rotate**.
|
|
25
25
|
- **From CLI:** `apimapper_credential_*` tools do **not** rotate MCP keys
|
|
26
26
|
themselves — by design, key rotation is an admin-UI gesture (not a
|
|
27
27
|
tool the LLM can call) to prevent confused-deputy rotation. Rotation
|
|
@@ -68,25 +68,6 @@ signing key. The user-visible site title (rendered by the wizard from
|
|
|
68
68
|
the server's response, not the URL bar) gives one more verification
|
|
69
69
|
surface for the user.
|
|
70
70
|
|
|
71
|
-
## OAuth 2.0 PKCE (HTTP transport)
|
|
72
|
-
|
|
73
|
-
When `@wootsup/mcp` runs in HTTP mode:
|
|
74
|
-
|
|
75
|
-
- **Grants supported:** `authorization_code` with PKCE-S256 only.
|
|
76
|
-
- **Implicit grant:** disabled.
|
|
77
|
-
- **`code` grant without PKCE:** rejected with `invalid_request`.
|
|
78
|
-
- **Loopback callbacks only:** `redirect_uri` must be
|
|
79
|
-
`http://127.0.0.1:<port>/callback` or `http://localhost:<port>/callback`.
|
|
80
|
-
- **State param:** required and verified.
|
|
81
|
-
- **Origin header:** Bearer tokens are bound to the issuing site URL.
|
|
82
|
-
A request from a different `Origin` is rejected (`401`).
|
|
83
|
-
- **Token TTL:** 1 hour. Refresh tokens are not issued — the client
|
|
84
|
-
re-runs the auth flow.
|
|
85
|
-
|
|
86
|
-
See `src/server-http.ts` for the implementation and
|
|
87
|
-
`src/server-http.test.ts` (2 tests) + `src/transports/http.test.ts`
|
|
88
|
-
(10 tests) for the pinning.
|
|
89
|
-
|
|
90
71
|
## Keychain fallback security
|
|
91
72
|
|
|
92
73
|
When `@napi-rs/keyring` cannot reach an OS keychain:
|