@burtson-labs/host-kit 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +55 -0
- package/dist/backgroundTasks.d.ts +113 -0
- package/dist/backgroundTasks.d.ts.map +1 -0
- package/dist/backgroundTasks.js +137 -0
- package/dist/backgroundTasks.js.map +1 -0
- package/dist/checkpoints.d.ts +99 -0
- package/dist/checkpoints.d.ts.map +1 -0
- package/dist/checkpoints.js +227 -0
- package/dist/checkpoints.js.map +1 -0
- package/dist/hooks.d.ts +51 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +152 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -0
- package/dist/insights.d.ts +398 -0
- package/dist/insights.d.ts.map +1 -0
- package/dist/insights.js +1933 -0
- package/dist/insights.js.map +1 -0
- package/dist/mcp.d.ts +60 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +281 -0
- package/dist/mcp.js.map +1 -0
- package/dist/mcpConnectors.d.ts +108 -0
- package/dist/mcpConnectors.d.ts.map +1 -0
- package/dist/mcpConnectors.js +217 -0
- package/dist/mcpConnectors.js.map +1 -0
- package/dist/mcpToolCache.d.ts +43 -0
- package/dist/mcpToolCache.d.ts.map +1 -0
- package/dist/mcpToolCache.js +150 -0
- package/dist/mcpToolCache.js.map +1 -0
- package/dist/mcpTrust.d.ts +22 -0
- package/dist/mcpTrust.d.ts.map +1 -0
- package/dist/mcpTrust.js +104 -0
- package/dist/mcpTrust.js.map +1 -0
- package/dist/memory.d.ts +38 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +151 -0
- package/dist/memory.js.map +1 -0
- package/dist/memoryIndex.d.ts +38 -0
- package/dist/memoryIndex.d.ts.map +1 -0
- package/dist/memoryIndex.js +142 -0
- package/dist/memoryIndex.js.map +1 -0
- package/dist/mentions.d.ts +33 -0
- package/dist/mentions.d.ts.map +1 -0
- package/dist/mentions.js +126 -0
- package/dist/mentions.js.map +1 -0
- package/dist/ollamaModels.d.ts +36 -0
- package/dist/ollamaModels.d.ts.map +1 -0
- package/dist/ollamaModels.js +83 -0
- package/dist/ollamaModels.js.map +1 -0
- package/dist/permissions.d.ts +72 -0
- package/dist/permissions.d.ts.map +1 -0
- package/dist/permissions.js +271 -0
- package/dist/permissions.js.map +1 -0
- package/dist/tools/extraTools.d.ts +80 -0
- package/dist/tools/extraTools.d.ts.map +1 -0
- package/dist/tools/extraTools.js +471 -0
- package/dist/tools/extraTools.js.map +1 -0
- package/dist/tools/readMemoryTool.d.ts +3 -0
- package/dist/tools/readMemoryTool.d.ts.map +1 -0
- package/dist/tools/readMemoryTool.js +115 -0
- package/dist/tools/readMemoryTool.js.map +1 -0
- package/dist/tools/taskTool.d.ts +119 -0
- package/dist/tools/taskTool.d.ts.map +1 -0
- package/dist/tools/taskTool.js +466 -0
- package/dist/tools/taskTool.js.map +1 -0
- package/dist/tools/testRunTool.d.ts +59 -0
- package/dist/tools/testRunTool.d.ts.map +1 -0
- package/dist/tools/testRunTool.js +308 -0
- package/dist/tools/testRunTool.js.map +1 -0
- package/dist/turnLog.d.ts +89 -0
- package/dist/turnLog.d.ts.map +1 -0
- package/dist/turnLog.js +469 -0
- package/dist/turnLog.js.map +1 -0
- package/package.json +35 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Connector wizards — convenience helpers that turn a single user
|
|
4
|
+
* input (a token, a credential, etc.) into a fully-formed McpServerConfig
|
|
5
|
+
* for a well-known provider's official MCP server. Each wizard is a
|
|
6
|
+
* small, single-purpose function; the surfaces (CLI slash command,
|
|
7
|
+
* extension Connections panel) call into these to avoid duplicating
|
|
8
|
+
* the spawn-command shape across surfaces.
|
|
9
|
+
*
|
|
10
|
+
* Phase 3 starts with GitHub — it's the simplest viable wizard
|
|
11
|
+
* (single PAT, no OAuth callback). Slack / Google / Microsoft follow
|
|
12
|
+
* as they each ship.
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.buildGitHubServerConfig = buildGitHubServerConfig;
|
|
16
|
+
exports.looksLikeGitHubToken = looksLikeGitHubToken;
|
|
17
|
+
exports.buildSlackServerConfig = buildSlackServerConfig;
|
|
18
|
+
exports.looksLikeSlackToken = looksLikeSlackToken;
|
|
19
|
+
exports.looksLikeSlackTeamId = looksLikeSlackTeamId;
|
|
20
|
+
exports.buildGitLabServerConfig = buildGitLabServerConfig;
|
|
21
|
+
exports.looksLikeGitLabToken = looksLikeGitLabToken;
|
|
22
|
+
exports.buildGmailServerConfig = buildGmailServerConfig;
|
|
23
|
+
exports.looksLikeGmailCredentialsPath = looksLikeGmailCredentialsPath;
|
|
24
|
+
exports.buildCustomServerConfig = buildCustomServerConfig;
|
|
25
|
+
/**
|
|
26
|
+
* Build a server config that runs `@modelcontextprotocol/server-github`
|
|
27
|
+
* via npx with the user's PAT. The token never lands in the config
|
|
28
|
+
* file's `args` (those are sometimes logged by editors); it goes into
|
|
29
|
+
* the env block which Bandit explicitly excludes from the trust
|
|
30
|
+
* fingerprint and from any UI surfaces that echo configs back.
|
|
31
|
+
*
|
|
32
|
+
* Activation defaults to "on-mention" with the standard github
|
|
33
|
+
* triggers so the GitHub server's tools only land in the prompt
|
|
34
|
+
* budget when the user actually mentions GitHub-y things.
|
|
35
|
+
*/
|
|
36
|
+
function buildGitHubServerConfig(token) {
|
|
37
|
+
const trimmed = token.trim();
|
|
38
|
+
if (!trimmed)
|
|
39
|
+
throw new Error('GitHub PAT is required');
|
|
40
|
+
return {
|
|
41
|
+
command: 'npx',
|
|
42
|
+
args: ['-y', '@modelcontextprotocol/server-github'],
|
|
43
|
+
env: { GITHUB_PERSONAL_ACCESS_TOKEN: trimmed },
|
|
44
|
+
activation: 'on-mention'
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Reasonable token-shape sanity check. GitHub PATs come in three
|
|
49
|
+
* flavors: classic (`ghp_…`, ~40 chars), fine-grained (`github_pat_…`,
|
|
50
|
+
* ~93 chars), and OAuth (`gho_…`). All start with a known prefix.
|
|
51
|
+
* We're not validating against the API here — just catching the
|
|
52
|
+
* fat-finger "I pasted my SSH key" failure mode before the user
|
|
53
|
+
* gets a confusing 401 from the server.
|
|
54
|
+
*/
|
|
55
|
+
function looksLikeGitHubToken(token) {
|
|
56
|
+
const t = token.trim();
|
|
57
|
+
return /^(ghp_|gho_|ghs_|ghu_|github_pat_)/.test(t);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Build a server config that runs `@modelcontextprotocol/server-slack`
|
|
61
|
+
* via npx. Slack's MCP server requires two env vars: a Bot User OAuth
|
|
62
|
+
* Token (`xoxb-…`) and the workspace's Team ID (`T…`). We ship the
|
|
63
|
+
* wizard with `activation: 'on-mention'` so Slack tools only land in
|
|
64
|
+
* the prompt budget when the user actually mentions slack/channel/
|
|
65
|
+
* message — the standard auto-derived triggers from activation.ts.
|
|
66
|
+
*/
|
|
67
|
+
function buildSlackServerConfig(botToken, teamId) {
|
|
68
|
+
const tk = botToken.trim();
|
|
69
|
+
const team = teamId.trim();
|
|
70
|
+
if (!tk)
|
|
71
|
+
throw new Error('Slack bot token (xoxb-…) is required');
|
|
72
|
+
if (!team)
|
|
73
|
+
throw new Error('Slack team ID (T…) is required');
|
|
74
|
+
return {
|
|
75
|
+
command: 'npx',
|
|
76
|
+
args: ['-y', '@modelcontextprotocol/server-slack'],
|
|
77
|
+
env: { SLACK_BOT_TOKEN: tk, SLACK_TEAM_ID: team },
|
|
78
|
+
activation: 'on-mention'
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/** Slack Bot User OAuth tokens always start with "xoxb-". User tokens
|
|
82
|
+
* are "xoxp-"; we deliberately accept both since the MCP server
|
|
83
|
+
* works with either, but the docs recommend bot tokens. */
|
|
84
|
+
function looksLikeSlackToken(token) {
|
|
85
|
+
return /^xox[bp]-/.test(token.trim());
|
|
86
|
+
}
|
|
87
|
+
/** Slack workspace IDs start with "T" followed by alphanumerics. We
|
|
88
|
+
* accept any non-empty string starting with T to allow for future
|
|
89
|
+
* changes in Slack's ID format. */
|
|
90
|
+
function looksLikeSlackTeamId(teamId) {
|
|
91
|
+
return /^T[A-Z0-9]+$/i.test(teamId.trim());
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Build a server config that runs `@modelcontextprotocol/server-gitlab`
|
|
95
|
+
* via npx. Defaults to GitLab.com's API; users with self-hosted
|
|
96
|
+
* GitLab can pass the API base URL through GITLAB_API_URL.
|
|
97
|
+
*/
|
|
98
|
+
function buildGitLabServerConfig(token, apiUrl) {
|
|
99
|
+
const trimmed = token.trim();
|
|
100
|
+
if (!trimmed)
|
|
101
|
+
throw new Error('GitLab personal access token is required');
|
|
102
|
+
const env = {
|
|
103
|
+
GITLAB_PERSONAL_ACCESS_TOKEN: trimmed
|
|
104
|
+
};
|
|
105
|
+
const trimmedUrl = (apiUrl ?? '').trim();
|
|
106
|
+
if (trimmedUrl)
|
|
107
|
+
env.GITLAB_API_URL = trimmedUrl;
|
|
108
|
+
return {
|
|
109
|
+
command: 'npx',
|
|
110
|
+
args: ['-y', '@modelcontextprotocol/server-gitlab'],
|
|
111
|
+
env,
|
|
112
|
+
activation: 'on-mention'
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/** GitLab PATs are typically 20 chars of base64-ish content with a
|
|
116
|
+
* `glpat-` prefix on newer tokens (since GitLab 14.5). We accept
|
|
117
|
+
* any non-empty string ≥ 20 chars to support older tokens too. */
|
|
118
|
+
function looksLikeGitLabToken(token) {
|
|
119
|
+
const t = token.trim();
|
|
120
|
+
if (t.startsWith('glpat-'))
|
|
121
|
+
return true;
|
|
122
|
+
return t.length >= 20 && /^[A-Za-z0-9_-]+$/.test(t);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Build a server config that runs `@gongrzhe/server-gmail-autoauth-mcp`
|
|
126
|
+
* via npx with the user's downloaded Google Cloud OAuth credentials.
|
|
127
|
+
* the first Phase 3 Google-flavor wizard, closing the gap
|
|
128
|
+
* memo'd in the 2026-04-29 MCP roadmap (Office 365 / Google / Slack
|
|
129
|
+
* / GitHub all named as Phase 3 targets; GitHub + Slack + GitLab
|
|
130
|
+
* shipped, Google was the last hold-out).
|
|
131
|
+
*
|
|
132
|
+
* Why this server: it's the most-cited community Gmail MCP server,
|
|
133
|
+
* speaks the standard stdio MCP shape, and handles the OAuth dance
|
|
134
|
+
* (browser popup → save refresh token) automatically on first spawn.
|
|
135
|
+
* Anthropic doesn't ship a `server-gmail` from the official MCP repo,
|
|
136
|
+
* so picking a community implementation is unavoidable — gongrzhe is
|
|
137
|
+
* well-maintained as of this writing.
|
|
138
|
+
*
|
|
139
|
+
* Setup the user has to do first (we can't):
|
|
140
|
+
* 1. Create an OAuth 2.0 client in https://console.cloud.google.com/
|
|
141
|
+
* Type: Desktop app. Enable the Gmail API on the project first.
|
|
142
|
+
* 2. Download the credentials JSON (the "OAuth client" download).
|
|
143
|
+
* 3. Pass that file path to this wizard.
|
|
144
|
+
*
|
|
145
|
+
* The first Bandit turn that touches Gmail will open a browser for the
|
|
146
|
+
* Google consent screen. After that the server caches a refresh token
|
|
147
|
+
* to `~/.gmail-mcp/credentials.json` and runs unattended.
|
|
148
|
+
*/
|
|
149
|
+
function buildGmailServerConfig(credentialsPath) {
|
|
150
|
+
const trimmed = credentialsPath.trim();
|
|
151
|
+
if (!trimmed)
|
|
152
|
+
throw new Error('Path to Google OAuth credentials JSON is required');
|
|
153
|
+
return {
|
|
154
|
+
command: 'npx',
|
|
155
|
+
args: ['-y', '@gongrzhe/server-gmail-autoauth-mcp'],
|
|
156
|
+
env: {
|
|
157
|
+
// The gongrzhe server reads GMAIL_OAUTH_PATH if set, otherwise
|
|
158
|
+
// defaults to ~/.gmail-mcp/gcp-oauth.keys.json. Pinning the env
|
|
159
|
+
// var so the wizard doesn't depend on the user pre-creating
|
|
160
|
+
// that directory or copying the credentials there manually.
|
|
161
|
+
GMAIL_OAUTH_PATH: trimmed
|
|
162
|
+
},
|
|
163
|
+
activation: 'on-mention'
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
/** Light shape check: the user pointed at a path that looks like a
|
|
167
|
+
* Google OAuth credentials JSON. We don't open the file here (host-
|
|
168
|
+
* kit stays filesystem-agnostic for testability); the wizard layer
|
|
169
|
+
* does the actual fs check before calling this. */
|
|
170
|
+
function looksLikeGmailCredentialsPath(p) {
|
|
171
|
+
const t = p.trim();
|
|
172
|
+
if (!t)
|
|
173
|
+
return false;
|
|
174
|
+
// Must look like a real path AND end in .json. Reject obvious garbage
|
|
175
|
+
// (URLs, single words) so users don't paste a token by mistake.
|
|
176
|
+
if (!/\.json$/i.test(t))
|
|
177
|
+
return false;
|
|
178
|
+
return t.includes('/') || t.includes('\\') || t.startsWith('~');
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Build a fully-custom server config from raw inputs. Used by the
|
|
182
|
+
* "+ Custom" wizard tile so users can register any MCP server (Linear,
|
|
183
|
+
* Jira, Bitbucket, Sentry, Postgres, an internal one they wrote) without
|
|
184
|
+
* waiting for Bandit to ship a dedicated wizard for that provider.
|
|
185
|
+
*
|
|
186
|
+
* `envInput` accepts the standard `KEY=VALUE` shape one-per-line, the
|
|
187
|
+
* format every CLI / dotfile uses, so users can paste from a `.env`.
|
|
188
|
+
*/
|
|
189
|
+
function buildCustomServerConfig(params) {
|
|
190
|
+
const cmd = params.command.trim();
|
|
191
|
+
if (!cmd)
|
|
192
|
+
throw new Error('Command is required');
|
|
193
|
+
const env = {};
|
|
194
|
+
if (params.envInput) {
|
|
195
|
+
for (const rawLine of params.envInput.split(/\r?\n/)) {
|
|
196
|
+
const line = rawLine.trim();
|
|
197
|
+
if (!line || line.startsWith('#'))
|
|
198
|
+
continue;
|
|
199
|
+
const eq = line.indexOf('=');
|
|
200
|
+
if (eq <= 0)
|
|
201
|
+
continue; // require a non-empty key before =
|
|
202
|
+
const key = line.slice(0, eq).trim();
|
|
203
|
+
const value = line.slice(eq + 1).trim();
|
|
204
|
+
// Strip surrounding quotes (matches dotenv loader semantics) so
|
|
205
|
+
// copy-paste from a .env file Just Works™.
|
|
206
|
+
const unquoted = value.replace(/^["']|["']$/g, '');
|
|
207
|
+
env[key] = unquoted;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
command: cmd,
|
|
212
|
+
args: params.args && params.args.length > 0 ? params.args : undefined,
|
|
213
|
+
env: Object.keys(env).length > 0 ? env : undefined,
|
|
214
|
+
activation: params.activation ?? 'on-mention'
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=mcpConnectors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpConnectors.js","sourceRoot":"","sources":["../src/mcpConnectors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tool-list cache — persists each server's discovered tool list
|
|
3
|
+
* keyed by config fingerprint so subsequent Bandit sessions don't have
|
|
4
|
+
* to spawn the server just to enumerate. The agent's per-turn registry
|
|
5
|
+
* build calls `discoverTools` for every registered server; when the
|
|
6
|
+
* cache primes the pool's in-memory copy, that call returns instantly
|
|
7
|
+
* without touching the child process — which is what fires the trust
|
|
8
|
+
* gate even on prompts that never use any MCP tool.
|
|
9
|
+
*
|
|
10
|
+
* Stored at `~/.bandit/mcp-tool-cache.json`. The file is non-sensitive
|
|
11
|
+
* (just tool metadata), so it's written with normal permissions —
|
|
12
|
+
* unlike `mcp-trust.json` which carries a security decision and lives
|
|
13
|
+
* at 0600. If the cache file is missing, corrupted, or stale (config
|
|
14
|
+
* fingerprint no longer matches), the pool falls back to a live spawn
|
|
15
|
+
* + listTools — same path as a fresh install. No correctness risk.
|
|
16
|
+
*/
|
|
17
|
+
import type { McpRemoteToolDef } from '@burtson-labs/agent-core';
|
|
18
|
+
/**
|
|
19
|
+
* Load every cached tool list as a Map keyed by fingerprint. Callers
|
|
20
|
+
* iterate registered servers and prime the pool when a fingerprint
|
|
21
|
+
* match exists. Missing-file / malformed-file paths return an empty
|
|
22
|
+
* map silently — Bandit must boot even when the cache is broken.
|
|
23
|
+
*/
|
|
24
|
+
export declare function loadMcpToolCache(): Promise<Map<string, McpRemoteToolDef[]>>;
|
|
25
|
+
/**
|
|
26
|
+
* Persist one server's tool list. Replaces the entry that shares the
|
|
27
|
+
* same fingerprint; never grows unbounded because fingerprint changes
|
|
28
|
+
* map to fresh entries and the old fingerprint's entry is dropped
|
|
29
|
+
* (covered by `pruneStale` when the host knows the current fingerprints).
|
|
30
|
+
*
|
|
31
|
+
* Failures are swallowed so a write-permission issue on `~/.bandit/`
|
|
32
|
+
* doesn't crash the turn — the pool will just re-discover next session.
|
|
33
|
+
*/
|
|
34
|
+
export declare function saveMcpToolEntry(name: string, fingerprint: string, tools: McpRemoteToolDef[]): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Drop cache entries whose fingerprints aren't in the currently-active
|
|
37
|
+
* set. Called on boot after the pool registers every server so stale
|
|
38
|
+
* entries from removed or reconfigured servers don't accumulate.
|
|
39
|
+
*/
|
|
40
|
+
export declare function pruneMcpToolCache(activeFingerprints: Set<string>): Promise<void>;
|
|
41
|
+
/** Path to the cache file — exposed for /mcp tools-cache commands. */
|
|
42
|
+
export declare function mcpToolCachePath(): string;
|
|
43
|
+
//# sourceMappingURL=mcpToolCache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpToolCache.d.ts","sourceRoot":"","sources":["../src/mcpToolCache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AA2DjE;;;;;GAKG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAOjF;AAED;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,gBAAgB,EAAE,GACxB,OAAO,CAAC,IAAI,CAAC,CASf;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,kBAAkB,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAStF;AAED,sEAAsE;AACtE,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MCP tool-list cache — persists each server's discovered tool list
|
|
4
|
+
* keyed by config fingerprint so subsequent Bandit sessions don't have
|
|
5
|
+
* to spawn the server just to enumerate. The agent's per-turn registry
|
|
6
|
+
* build calls `discoverTools` for every registered server; when the
|
|
7
|
+
* cache primes the pool's in-memory copy, that call returns instantly
|
|
8
|
+
* without touching the child process — which is what fires the trust
|
|
9
|
+
* gate even on prompts that never use any MCP tool.
|
|
10
|
+
*
|
|
11
|
+
* Stored at `~/.bandit/mcp-tool-cache.json`. The file is non-sensitive
|
|
12
|
+
* (just tool metadata), so it's written with normal permissions —
|
|
13
|
+
* unlike `mcp-trust.json` which carries a security decision and lives
|
|
14
|
+
* at 0600. If the cache file is missing, corrupted, or stale (config
|
|
15
|
+
* fingerprint no longer matches), the pool falls back to a live spawn
|
|
16
|
+
* + listTools — same path as a fresh install. No correctness risk.
|
|
17
|
+
*/
|
|
18
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
21
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
22
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
23
|
+
}
|
|
24
|
+
Object.defineProperty(o, k2, desc);
|
|
25
|
+
}) : (function(o, m, k, k2) {
|
|
26
|
+
if (k2 === undefined) k2 = k;
|
|
27
|
+
o[k2] = m[k];
|
|
28
|
+
}));
|
|
29
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
30
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
31
|
+
}) : function(o, v) {
|
|
32
|
+
o["default"] = v;
|
|
33
|
+
});
|
|
34
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
35
|
+
var ownKeys = function(o) {
|
|
36
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
37
|
+
var ar = [];
|
|
38
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
39
|
+
return ar;
|
|
40
|
+
};
|
|
41
|
+
return ownKeys(o);
|
|
42
|
+
};
|
|
43
|
+
return function (mod) {
|
|
44
|
+
if (mod && mod.__esModule) return mod;
|
|
45
|
+
var result = {};
|
|
46
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
47
|
+
__setModuleDefault(result, mod);
|
|
48
|
+
return result;
|
|
49
|
+
};
|
|
50
|
+
})();
|
|
51
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
52
|
+
exports.loadMcpToolCache = loadMcpToolCache;
|
|
53
|
+
exports.saveMcpToolEntry = saveMcpToolEntry;
|
|
54
|
+
exports.pruneMcpToolCache = pruneMcpToolCache;
|
|
55
|
+
exports.mcpToolCachePath = mcpToolCachePath;
|
|
56
|
+
const fs = __importStar(require("fs"));
|
|
57
|
+
const os = __importStar(require("os"));
|
|
58
|
+
const path = __importStar(require("path"));
|
|
59
|
+
/**
|
|
60
|
+
* Resolved per-call rather than cached at module load so tests that
|
|
61
|
+
* mutate `process.env.HOME` actually hit the tmp directory they set
|
|
62
|
+
* up — and so a long-running host that's had HOME re-pointed for any
|
|
63
|
+
* reason doesn't go on writing to a stale path.
|
|
64
|
+
*/
|
|
65
|
+
function cacheFilePath() {
|
|
66
|
+
return path.join(os.homedir(), '.bandit', 'mcp-tool-cache.json');
|
|
67
|
+
}
|
|
68
|
+
async function readCacheFile() {
|
|
69
|
+
try {
|
|
70
|
+
const raw = await fs.promises.readFile(cacheFilePath(), 'utf-8');
|
|
71
|
+
const parsed = JSON.parse(raw);
|
|
72
|
+
if (parsed.version === 1 && Array.isArray(parsed.entries)) {
|
|
73
|
+
return {
|
|
74
|
+
version: 1,
|
|
75
|
+
entries: parsed.entries.filter((e) => typeof e === 'object' && e !== null &&
|
|
76
|
+
typeof e.fingerprint === 'string' &&
|
|
77
|
+
Array.isArray(e.tools))
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// Missing or malformed — return empty. The pool will re-spawn on
|
|
83
|
+
// first discoverTools and we'll repopulate from there.
|
|
84
|
+
}
|
|
85
|
+
return { version: 1, entries: [] };
|
|
86
|
+
}
|
|
87
|
+
async function writeCacheFile(file) {
|
|
88
|
+
const dir = path.dirname(cacheFilePath());
|
|
89
|
+
try {
|
|
90
|
+
await fs.promises.mkdir(dir, { recursive: true });
|
|
91
|
+
}
|
|
92
|
+
catch { /* exists */ }
|
|
93
|
+
await fs.promises.writeFile(cacheFilePath(), JSON.stringify(file, null, 2), 'utf-8');
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Load every cached tool list as a Map keyed by fingerprint. Callers
|
|
97
|
+
* iterate registered servers and prime the pool when a fingerprint
|
|
98
|
+
* match exists. Missing-file / malformed-file paths return an empty
|
|
99
|
+
* map silently — Bandit must boot even when the cache is broken.
|
|
100
|
+
*/
|
|
101
|
+
async function loadMcpToolCache() {
|
|
102
|
+
const file = await readCacheFile();
|
|
103
|
+
const map = new Map();
|
|
104
|
+
for (const entry of file.entries) {
|
|
105
|
+
map.set(entry.fingerprint, entry.tools);
|
|
106
|
+
}
|
|
107
|
+
return map;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Persist one server's tool list. Replaces the entry that shares the
|
|
111
|
+
* same fingerprint; never grows unbounded because fingerprint changes
|
|
112
|
+
* map to fresh entries and the old fingerprint's entry is dropped
|
|
113
|
+
* (covered by `pruneStale` when the host knows the current fingerprints).
|
|
114
|
+
*
|
|
115
|
+
* Failures are swallowed so a write-permission issue on `~/.bandit/`
|
|
116
|
+
* doesn't crash the turn — the pool will just re-discover next session.
|
|
117
|
+
*/
|
|
118
|
+
async function saveMcpToolEntry(name, fingerprint, tools) {
|
|
119
|
+
try {
|
|
120
|
+
const file = await readCacheFile();
|
|
121
|
+
const next = file.entries.filter((e) => e.fingerprint !== fingerprint);
|
|
122
|
+
next.push({ name, fingerprint, tools, updatedAt: new Date().toISOString() });
|
|
123
|
+
await writeCacheFile({ version: 1, entries: next });
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// Best-effort cache — agent loop continues regardless.
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Drop cache entries whose fingerprints aren't in the currently-active
|
|
131
|
+
* set. Called on boot after the pool registers every server so stale
|
|
132
|
+
* entries from removed or reconfigured servers don't accumulate.
|
|
133
|
+
*/
|
|
134
|
+
async function pruneMcpToolCache(activeFingerprints) {
|
|
135
|
+
try {
|
|
136
|
+
const file = await readCacheFile();
|
|
137
|
+
const kept = file.entries.filter((e) => activeFingerprints.has(e.fingerprint));
|
|
138
|
+
if (kept.length === file.entries.length)
|
|
139
|
+
return;
|
|
140
|
+
await writeCacheFile({ version: 1, entries: kept });
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// Best-effort cleanup.
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/** Path to the cache file — exposed for /mcp tools-cache commands. */
|
|
147
|
+
function mcpToolCachePath() {
|
|
148
|
+
return cacheFilePath();
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=mcpToolCache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpToolCache.js","sourceRoot":"","sources":["../src/mcpToolCache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP trust store — remembers which server-config fingerprints the user
|
|
3
|
+
* has approved across Bandit sessions. Spawning an MCP server is a
|
|
4
|
+
* code-execution boundary; the first time we encounter a config we
|
|
5
|
+
* haven't seen, the host asks the user. The answer (allow once / allow
|
|
6
|
+
* always / deny) is the host's responsibility to surface; this module
|
|
7
|
+
* is the persistence layer for "allow always".
|
|
8
|
+
*
|
|
9
|
+
* Stored at `~/.bandit/mcp-trust.json` as a flat array of fingerprints
|
|
10
|
+
* — file is written 0600 since it represents a security decision the
|
|
11
|
+
* user shouldn't have casually edited by another process.
|
|
12
|
+
*/
|
|
13
|
+
/** Read the set of fingerprints the user has previously approved. */
|
|
14
|
+
export declare function loadApprovedMcpFingerprints(): Promise<Set<string>>;
|
|
15
|
+
/** Persist a fingerprint as "always allowed". Idempotent — duplicates are dropped. */
|
|
16
|
+
export declare function approveMcpFingerprint(fingerprint: string): Promise<void>;
|
|
17
|
+
/** Remove a fingerprint from the approved list (used by a future revoke flow). */
|
|
18
|
+
export declare function revokeMcpFingerprint(fingerprint: string): Promise<void>;
|
|
19
|
+
/** Path to the trust file — exposed so error messages and CLI helpers
|
|
20
|
+
* can echo the location for the user. */
|
|
21
|
+
export declare function mcpTrustPath(): string;
|
|
22
|
+
//# sourceMappingURL=mcpTrust.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpTrust.d.ts","sourceRoot":"","sources":["../src/mcpTrust.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAsCH,qEAAqE;AACrE,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAGxE;AAED,sFAAsF;AACtF,wBAAsB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM9E;AAED,kFAAkF;AAClF,wBAAsB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM7E;AAED;0CAC0C;AAC1C,wBAAgB,YAAY,IAAI,MAAM,CAErC"}
|
package/dist/mcpTrust.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MCP trust store — remembers which server-config fingerprints the user
|
|
4
|
+
* has approved across Bandit sessions. Spawning an MCP server is a
|
|
5
|
+
* code-execution boundary; the first time we encounter a config we
|
|
6
|
+
* haven't seen, the host asks the user. The answer (allow once / allow
|
|
7
|
+
* always / deny) is the host's responsibility to surface; this module
|
|
8
|
+
* is the persistence layer for "allow always".
|
|
9
|
+
*
|
|
10
|
+
* Stored at `~/.bandit/mcp-trust.json` as a flat array of fingerprints
|
|
11
|
+
* — file is written 0600 since it represents a security decision the
|
|
12
|
+
* user shouldn't have casually edited by another process.
|
|
13
|
+
*/
|
|
14
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
17
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
18
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
19
|
+
}
|
|
20
|
+
Object.defineProperty(o, k2, desc);
|
|
21
|
+
}) : (function(o, m, k, k2) {
|
|
22
|
+
if (k2 === undefined) k2 = k;
|
|
23
|
+
o[k2] = m[k];
|
|
24
|
+
}));
|
|
25
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
26
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
27
|
+
}) : function(o, v) {
|
|
28
|
+
o["default"] = v;
|
|
29
|
+
});
|
|
30
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
31
|
+
var ownKeys = function(o) {
|
|
32
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
33
|
+
var ar = [];
|
|
34
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
35
|
+
return ar;
|
|
36
|
+
};
|
|
37
|
+
return ownKeys(o);
|
|
38
|
+
};
|
|
39
|
+
return function (mod) {
|
|
40
|
+
if (mod && mod.__esModule) return mod;
|
|
41
|
+
var result = {};
|
|
42
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
43
|
+
__setModuleDefault(result, mod);
|
|
44
|
+
return result;
|
|
45
|
+
};
|
|
46
|
+
})();
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.loadApprovedMcpFingerprints = loadApprovedMcpFingerprints;
|
|
49
|
+
exports.approveMcpFingerprint = approveMcpFingerprint;
|
|
50
|
+
exports.revokeMcpFingerprint = revokeMcpFingerprint;
|
|
51
|
+
exports.mcpTrustPath = mcpTrustPath;
|
|
52
|
+
const fs = __importStar(require("fs"));
|
|
53
|
+
const os = __importStar(require("os"));
|
|
54
|
+
const path = __importStar(require("path"));
|
|
55
|
+
const TRUST_FILE = path.join(os.homedir(), '.bandit', 'mcp-trust.json');
|
|
56
|
+
async function readTrustFile() {
|
|
57
|
+
try {
|
|
58
|
+
const raw = await fs.promises.readFile(TRUST_FILE, 'utf-8');
|
|
59
|
+
const parsed = JSON.parse(raw);
|
|
60
|
+
if (Array.isArray(parsed.approved)) {
|
|
61
|
+
return { approved: parsed.approved.filter((s) => typeof s === 'string') };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Missing or malformed — return empty. Trust is opt-in; absence
|
|
66
|
+
// of the file means no fingerprints have been approved yet.
|
|
67
|
+
}
|
|
68
|
+
return { approved: [] };
|
|
69
|
+
}
|
|
70
|
+
async function writeTrustFile(file) {
|
|
71
|
+
const dir = path.dirname(TRUST_FILE);
|
|
72
|
+
try {
|
|
73
|
+
await fs.promises.mkdir(dir, { recursive: true });
|
|
74
|
+
}
|
|
75
|
+
catch { /* exists */ }
|
|
76
|
+
await fs.promises.writeFile(TRUST_FILE, JSON.stringify(file, null, 2), { encoding: 'utf-8', mode: 0o600 });
|
|
77
|
+
}
|
|
78
|
+
/** Read the set of fingerprints the user has previously approved. */
|
|
79
|
+
async function loadApprovedMcpFingerprints() {
|
|
80
|
+
const file = await readTrustFile();
|
|
81
|
+
return new Set(file.approved);
|
|
82
|
+
}
|
|
83
|
+
/** Persist a fingerprint as "always allowed". Idempotent — duplicates are dropped. */
|
|
84
|
+
async function approveMcpFingerprint(fingerprint) {
|
|
85
|
+
const file = await readTrustFile();
|
|
86
|
+
if (!file.approved.includes(fingerprint)) {
|
|
87
|
+
file.approved.push(fingerprint);
|
|
88
|
+
await writeTrustFile(file);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/** Remove a fingerprint from the approved list (used by a future revoke flow). */
|
|
92
|
+
async function revokeMcpFingerprint(fingerprint) {
|
|
93
|
+
const file = await readTrustFile();
|
|
94
|
+
const next = file.approved.filter((f) => f !== fingerprint);
|
|
95
|
+
if (next.length !== file.approved.length) {
|
|
96
|
+
await writeTrustFile({ approved: next });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/** Path to the trust file — exposed so error messages and CLI helpers
|
|
100
|
+
* can echo the location for the user. */
|
|
101
|
+
function mcpTrustPath() {
|
|
102
|
+
return TRUST_FILE;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=mcpTrust.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpTrust.js","sourceRoot":"","sources":["../src/mcpTrust.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCH,kEAGC;AAGD,sDAMC;AAGD,oDAMC;AAID,oCAEC;AAhED,uCAAyB;AACzB,uCAAyB;AACzB,2CAA6B;AAE7B,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,gBAAgB,CAAC,CAAC;AAQxE,KAAK,UAAU,aAAa;IAC1B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC5D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAuB,CAAC;QACrD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YACnC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,EAAE,CAAC;QAC5E,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,gEAAgE;QAChE,4DAA4D;IAC9D,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;AAC1B,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,IAAe;IAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACrC,IAAI,CAAC;QAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IACjF,MAAM,EAAE,CAAC,QAAQ,CAAC,SAAS,CACzB,UAAU,EACV,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAC7B,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CACnC,CAAC;AACJ,CAAC;AAED,qEAAqE;AAC9D,KAAK,UAAU,2BAA2B;IAC/C,MAAM,IAAI,GAAG,MAAM,aAAa,EAAE,CAAC;IACnC,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC;AAED,sFAAsF;AAC/E,KAAK,UAAU,qBAAqB,CAAC,WAAmB;IAC7D,MAAM,IAAI,GAAG,MAAM,aAAa,EAAE,CAAC;IACnC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QACzC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAChC,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;AACH,CAAC;AAED,kFAAkF;AAC3E,KAAK,UAAU,oBAAoB,CAAC,WAAmB;IAC5D,MAAM,IAAI,GAAG,MAAM,aAAa,EAAE,CAAC;IACnC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,WAAW,CAAC,CAAC;IAC5D,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;QACzC,MAAM,cAAc,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,CAAC;AACH,CAAC;AAED;0CAC0C;AAC1C,SAAgB,YAAY;IAC1B,OAAO,UAAU,CAAC;AACpB,CAAC"}
|
package/dist/memory.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory — auto-load workspace-local context files (BANDIT.md / CLAUDE.md)
|
|
3
|
+
* and inline them into the system prompt so the agent follows project rules
|
|
4
|
+
* without being re-told on every turn.
|
|
5
|
+
*/
|
|
6
|
+
import { type MemoryWarnFn } from './memoryIndex';
|
|
7
|
+
export interface MemoryBundle {
|
|
8
|
+
/** Combined text ready for injection. Empty string if nothing found. */
|
|
9
|
+
content: string;
|
|
10
|
+
sources: string[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* loadMemory + MEMORY.md index, fused into a single bundle the host can
|
|
14
|
+
* inject as the system-prompt memory block. Always-loaded files come
|
|
15
|
+
* first; the index follows under its own source marker so the model
|
|
16
|
+
* knows which entries are full content vs. on-demand topic pointers.
|
|
17
|
+
*
|
|
18
|
+
* Returns sources from BOTH passes when present — callers use this for
|
|
19
|
+
* the `/memory` slash command and boot-status output.
|
|
20
|
+
*/
|
|
21
|
+
export declare function loadCombinedMemory(cwd: string, warn?: MemoryWarnFn): Promise<MemoryBundle>;
|
|
22
|
+
export declare function loadMemory(cwd: string): Promise<MemoryBundle>;
|
|
23
|
+
/**
|
|
24
|
+
* Append a single fact to project memory so it survives across sessions.
|
|
25
|
+
*
|
|
26
|
+
* Persists to `BANDIT.md` at the workspace root (creates the file with a
|
|
27
|
+
* minimal frontmatter-free header when it doesn't exist). Bullets are
|
|
28
|
+
* appended under a `## Notes` heading so multiple `/remember` calls
|
|
29
|
+
* accumulate without scattering. CLAUDE.md-only repos still get found
|
|
30
|
+
* by loadMemory, but new facts always land in BANDIT.md to keep one
|
|
31
|
+
* canonical write target.
|
|
32
|
+
*
|
|
33
|
+
* Returns the absolute path written so the caller can echo it back to
|
|
34
|
+
* the user — visibility matters because the user typed "remember X"
|
|
35
|
+
* and needs to see WHERE the fact landed.
|
|
36
|
+
*/
|
|
37
|
+
export declare function appendMemory(cwd: string, fact: string): Promise<string>;
|
|
38
|
+
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../src/memory.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,eAAe,CAAC;AAWvB,MAAM,WAAW,YAAY;IAC3B,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAUhG;AAED,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAsBnE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkC7E"}
|