@adtl/gateway 0.3.0 → 0.3.2
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 +59 -1
- package/dist/audit.d.ts +4 -2
- package/dist/audit.d.ts.map +1 -1
- package/dist/audit.js.map +1 -1
- package/dist/cli.js +80 -15
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +49 -44
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +10 -3
- package/dist/config.js.map +1 -1
- package/dist/gateway.d.ts +1 -1
- package/dist/gateway.js +1 -1
- package/dist/hooks/claudeCode.d.ts +43 -0
- package/dist/hooks/claudeCode.d.ts.map +1 -0
- package/dist/hooks/claudeCode.js +148 -0
- package/dist/hooks/claudeCode.js.map +1 -0
- package/dist/hooks/index.d.ts +4 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +4 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/preset.d.ts +16 -0
- package/dist/hooks/preset.d.ts.map +1 -0
- package/dist/hooks/preset.js +32 -0
- package/dist/hooks/preset.js.map +1 -0
- package/dist/hooks/run.d.ts +9 -0
- package/dist/hooks/run.d.ts.map +1 -0
- package/dist/hooks/run.js +44 -0
- package/dist/hooks/run.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/init/fromMcpJson.d.ts +41 -0
- package/dist/init/fromMcpJson.d.ts.map +1 -0
- package/dist/init/fromMcpJson.js +141 -0
- package/dist/init/fromMcpJson.js.map +1 -0
- package/dist/init/index.d.ts +2 -0
- package/dist/init/index.d.ts.map +1 -1
- package/dist/init/index.js +2 -0
- package/dist/init/index.js.map +1 -1
- package/dist/init/run.d.ts +13 -0
- package/dist/init/run.d.ts.map +1 -1
- package/dist/init/run.js +83 -11
- package/dist/init/run.js.map +1 -1
- package/dist/startup.d.ts +12 -1
- package/dist/startup.d.ts.map +1 -1
- package/dist/startup.js +27 -3
- package/dist/startup.js.map +1 -1
- package/package.json +4 -4
- package/src/audit.ts +4 -2
- package/src/cli.ts +70 -15
- package/src/config.ts +9 -3
- package/src/gateway.ts +1 -1
- package/src/hooks/claudeCode.ts +194 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/preset.ts +47 -0
- package/src/hooks/run.ts +49 -0
- package/src/index.ts +1 -0
- package/src/init/fromMcpJson.ts +165 -0
- package/src/init/index.ts +2 -0
- package/src/init/run.ts +91 -12
- package/src/startup.ts +28 -4
package/dist/init/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { PRESETS, choosePreset, type Preset } from "./presets.js";
|
|
2
2
|
export { mergeIntoDocument, ruleFromHints, suggest, type MergeReport, type ServerScan, type ServerSuggestion, type ToolSuggestion } from "./suggest.js";
|
|
3
3
|
export { runInit, type InitOptions, type InitResult } from "./run.js";
|
|
4
|
+
export { DEFAULT_GATEWAY_NAME, gatewayEntry, importMcpServers, isGatewayEntry, toUpstream, upstreamId, type McpImport } from "./fromMcpJson.js";
|
|
5
|
+
export { NEW_CONFIG } from "./run.js";
|
|
4
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/init/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/init/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,KAAK,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AACxJ,OAAO,EAAE,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/init/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,KAAK,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AACxJ,OAAO,EAAE,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAChJ,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/init/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { PRESETS, choosePreset } from "./presets.js";
|
|
2
2
|
export { mergeIntoDocument, ruleFromHints, suggest } from "./suggest.js";
|
|
3
3
|
export { runInit } from "./run.js";
|
|
4
|
+
export { DEFAULT_GATEWAY_NAME, gatewayEntry, importMcpServers, isGatewayEntry, toUpstream, upstreamId } from "./fromMcpJson.js";
|
|
5
|
+
export { NEW_CONFIG } from "./run.js";
|
|
4
6
|
//# sourceMappingURL=index.js.map
|
package/dist/init/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/init/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAe,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,OAAO,EAAiF,MAAM,cAAc,CAAC;AACxJ,OAAO,EAAE,OAAO,EAAqC,MAAM,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/init/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAe,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,OAAO,EAAiF,MAAM,cAAc,CAAC;AACxJ,OAAO,EAAE,OAAO,EAAqC,MAAM,UAAU,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAkB,MAAM,kBAAkB,CAAC;AAChJ,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/init/run.d.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
import { type McpImport } from "./fromMcpJson.js";
|
|
1
2
|
import { type MergeReport } from "./suggest.js";
|
|
2
3
|
export interface InitOptions {
|
|
3
4
|
path: string;
|
|
4
5
|
write?: boolean;
|
|
5
6
|
env?: NodeJS.ProcessEnv;
|
|
7
|
+
/** An MCP client config with `mcpServers` (e.g. Claude Code's .mcp.json) to import upstreams from. */
|
|
8
|
+
from?: string;
|
|
9
|
+
/** Name for the gateway's entry in that client config. Default `adtl`. */
|
|
10
|
+
gatewayName?: string;
|
|
6
11
|
}
|
|
12
|
+
/** Starting point when `--from` creates adtl.yaml. */
|
|
13
|
+
export declare const NEW_CONFIG = "# ADTL config, drafted by `adtl-gateway init --from`. Review every rule marked \"review\".\n#\n# For every tool, say what its result is (integrity, confidentiality) and where\n# calling it can send data (egress). Anything left out gets the defaults below.\n\ndefaults:\n integrity: untrusted\n confidentiality: private\n\npolicy:\n default: allow\n rules:\n - name: no-public-write-after-untrusted\n when:\n session.integrity: untrusted\n tool.egress: [public, external, unknown]\n then: ask\n reason: Session has read untrusted content; confirm before sending data out\n\n - name: private-stays-private\n when:\n session.confidentiality: [private, secret]\n tool.egress: public\n then: block\n reason: Private data must not reach a public tool\n\ngateway:\n upstreams: {}\n";
|
|
7
14
|
export interface InitResult {
|
|
8
15
|
/** The updated adtl.yaml. */
|
|
9
16
|
text: string;
|
|
@@ -13,6 +20,12 @@ export interface InitResult {
|
|
|
13
20
|
id: string;
|
|
14
21
|
error: string;
|
|
15
22
|
}[];
|
|
23
|
+
/** With `from`: what was imported, and the rewritten client config. */
|
|
24
|
+
mcp?: McpImport & {
|
|
25
|
+
path: string;
|
|
26
|
+
};
|
|
27
|
+
/** True if adtl.yaml didn't exist and was drafted from scratch. */
|
|
28
|
+
created?: boolean;
|
|
16
29
|
}
|
|
17
30
|
export declare function runInit(opts: InitOptions): Promise<InitResult>;
|
|
18
31
|
//# sourceMappingURL=run.d.ts.map
|
package/dist/init/run.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/init/run.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/init/run.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAA8B,KAAK,WAAW,EAAmB,MAAM,cAAc,CAAC;AAE7F,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,sGAAsG;IACtG,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAKD,sDAAsD;AACtD,eAAO,MAAM,UAAU,g1BA4BtB,CAAC;AAEF,MAAM,WAAW,UAAU;IACzB,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,uEAAuE;IACvE,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxC,uEAAuE;IACvE,GAAG,CAAC,EAAE,SAAS,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACnC,mEAAmE;IACnE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAwBD,wBAAsB,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAwDpE"}
|
package/dist/init/run.js
CHANGED
|
@@ -2,16 +2,54 @@
|
|
|
2
2
|
* `adtl-gateway init`: connect to every upstream in adtl.yaml, list its
|
|
3
3
|
* tools, and draft a labeling rule for each tool not configured yet.
|
|
4
4
|
*
|
|
5
|
+
* With `from` (e.g. `.mcp.json`), the client's MCP servers are first imported
|
|
6
|
+
* into gateway.upstreams (see fromMcpJson.ts), and adtl.yaml is created if it
|
|
7
|
+
* doesn't exist yet.
|
|
8
|
+
*
|
|
5
9
|
* Without `write`, the updated file is only returned (the CLI prints it).
|
|
6
10
|
* With `write`, the original is kept as `<file>.bak` and the file is updated
|
|
7
|
-
* in place
|
|
11
|
+
* in place; with `from`, the client config is rewritten to use the gateway,
|
|
12
|
+
* also keeping a `.bak`. Existing entries and comments are never changed.
|
|
8
13
|
*/
|
|
9
14
|
import { readFile, writeFile } from "node:fs/promises";
|
|
10
|
-
import { dirname } from "node:path";
|
|
15
|
+
import { dirname, resolve } from "node:path";
|
|
11
16
|
import { parseDocument } from "yaml";
|
|
12
17
|
import { expandUpstream } from "../config.js";
|
|
13
|
-
import { connectUpstream,
|
|
18
|
+
import { GatewayConfigError, connectUpstream, parseGatewayConfigText } from "../startup.js";
|
|
19
|
+
import { importMcpServers } from "./fromMcpJson.js";
|
|
14
20
|
import { mergeIntoDocument, suggest } from "./suggest.js";
|
|
21
|
+
/** Don't fold long values (paths, commands) across lines. */
|
|
22
|
+
const YAML_OPTIONS = { lineWidth: 0 };
|
|
23
|
+
/** Starting point when `--from` creates adtl.yaml. */
|
|
24
|
+
export const NEW_CONFIG = `# ADTL config, drafted by \`adtl-gateway init --from\`. Review every rule marked "review".
|
|
25
|
+
#
|
|
26
|
+
# For every tool, say what its result is (integrity, confidentiality) and where
|
|
27
|
+
# calling it can send data (egress). Anything left out gets the defaults below.
|
|
28
|
+
|
|
29
|
+
defaults:
|
|
30
|
+
integrity: untrusted
|
|
31
|
+
confidentiality: private
|
|
32
|
+
|
|
33
|
+
policy:
|
|
34
|
+
default: allow
|
|
35
|
+
rules:
|
|
36
|
+
- name: no-public-write-after-untrusted
|
|
37
|
+
when:
|
|
38
|
+
session.integrity: untrusted
|
|
39
|
+
tool.egress: [public, external, unknown]
|
|
40
|
+
then: ask
|
|
41
|
+
reason: Session has read untrusted content; confirm before sending data out
|
|
42
|
+
|
|
43
|
+
- name: private-stays-private
|
|
44
|
+
when:
|
|
45
|
+
session.confidentiality: [private, secret]
|
|
46
|
+
tool.egress: public
|
|
47
|
+
then: block
|
|
48
|
+
reason: Private data must not reach a public tool
|
|
49
|
+
|
|
50
|
+
gateway:
|
|
51
|
+
upstreams: {}
|
|
52
|
+
`;
|
|
15
53
|
async function listAllTools(client) {
|
|
16
54
|
const tools = [];
|
|
17
55
|
let cursor;
|
|
@@ -35,9 +73,36 @@ async function scan(id, connect) {
|
|
|
35
73
|
}
|
|
36
74
|
export async function runInit(opts) {
|
|
37
75
|
const env = opts.env ?? process.env;
|
|
38
|
-
const
|
|
76
|
+
const path = resolve(opts.path);
|
|
77
|
+
let original;
|
|
78
|
+
try {
|
|
79
|
+
original = await readFile(path, "utf8");
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
if (err.code !== "ENOENT" || !opts.from) {
|
|
83
|
+
throw new GatewayConfigError(`Cannot read config file ${path}: ${err.message}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const doc = parseDocument(original ?? NEW_CONFIG);
|
|
87
|
+
let mcp;
|
|
88
|
+
if (opts.from) {
|
|
89
|
+
const mcpPath = resolve(opts.from);
|
|
90
|
+
let mcpText;
|
|
91
|
+
try {
|
|
92
|
+
mcpText = await readFile(mcpPath, "utf8");
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
throw new GatewayConfigError(`Cannot read ${mcpPath}: ${err.message}`);
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
mcp = { path: mcpPath, ...importMcpServers(mcpText, mcpPath, doc, path, opts.gatewayName) };
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
throw new GatewayConfigError(err.message);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const loaded = parseGatewayConfigText(doc.toString(YAML_OPTIONS), path);
|
|
39
105
|
const baseDir = dirname(loaded.path);
|
|
40
|
-
const original = await readFile(loaded.path, "utf8");
|
|
41
106
|
const ids = Object.keys(loaded.gateway.upstreams);
|
|
42
107
|
const settled = await Promise.allSettled(ids.map((id) => scan(id, () => connectUpstream(id, expandUpstream(id, loaded.gateway.upstreams[id], env), baseDir))));
|
|
43
108
|
const scans = [];
|
|
@@ -48,13 +113,20 @@ export async function runInit(opts) {
|
|
|
48
113
|
else
|
|
49
114
|
failed.push({ id: ids[i], error: s.reason.message });
|
|
50
115
|
});
|
|
51
|
-
const doc = parseDocument(original);
|
|
52
116
|
const reports = mergeIntoDocument(doc, scans.map((s) => suggest(s)));
|
|
53
|
-
const text = doc.toString();
|
|
54
|
-
if (opts.write
|
|
55
|
-
|
|
56
|
-
|
|
117
|
+
const text = doc.toString(YAML_OPTIONS);
|
|
118
|
+
if (opts.write) {
|
|
119
|
+
if (original === undefined)
|
|
120
|
+
await writeFile(path, text, "utf8");
|
|
121
|
+
else if (text !== original) {
|
|
122
|
+
await writeFile(`${path}.bak`, original, "utf8");
|
|
123
|
+
await writeFile(path, text, "utf8");
|
|
124
|
+
}
|
|
125
|
+
if (mcp?.changed) {
|
|
126
|
+
await writeFile(`${mcp.path}.bak`, await readFile(mcp.path, "utf8"), "utf8");
|
|
127
|
+
await writeFile(mcp.path, mcp.text, "utf8");
|
|
128
|
+
}
|
|
57
129
|
}
|
|
58
|
-
return { text, reports, failed };
|
|
130
|
+
return { text, reports, failed, ...(mcp ? { mcp } : {}), ...(original === undefined ? { created: true } : {}) };
|
|
59
131
|
}
|
|
60
132
|
//# sourceMappingURL=run.js.map
|
package/dist/init/run.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/init/run.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/init/run.ts"],"names":[],"mappings":"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"}
|
package/dist/startup.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
-
import type
|
|
2
|
+
import { type AdtlConfig } from "@adtl/labels";
|
|
3
3
|
import { ApprovalBroker } from "./approvals.js";
|
|
4
4
|
import { type GatewayConfig, type Upstream } from "./config.js";
|
|
5
5
|
import { AdtlGateway, type GatewayLogger } from "./gateway.js";
|
|
@@ -12,11 +12,22 @@ export interface LoadedConfig {
|
|
|
12
12
|
gateway: GatewayConfig;
|
|
13
13
|
}
|
|
14
14
|
export declare function loadGatewayConfig(path: string): Promise<LoadedConfig>;
|
|
15
|
+
/** Validate adtl.yaml text; `path` is where it lives (relative paths in it resolve against its directory). */
|
|
16
|
+
export declare function parseGatewayConfigText(text: string, path: string): LoadedConfig;
|
|
15
17
|
/** Path of the bundled adtl-interceptors CLI. */
|
|
16
18
|
export declare function bundledInterceptorsCli(): string;
|
|
17
19
|
export declare function connectUpstream(id: string, u: Upstream, baseDir: string): Promise<Client>;
|
|
18
20
|
/** Build the approval broker from `gateway.approvals`, or undefined if not configured. */
|
|
19
21
|
export declare function createApprovals(cfg: GatewayConfig, env: NodeJS.ProcessEnv, log: GatewayLogger): ApprovalBroker | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Under Claude Code, which passes `CLAUDE_CODE_SESSION_ID` to stdio MCP
|
|
24
|
+
* servers, the gateway uses that id and keeps session labels in files, so the
|
|
25
|
+
* Claude Code hooks (`adtl-gateway hook`) add to the same session label.
|
|
26
|
+
*/
|
|
27
|
+
export declare function sharedSession(env: NodeJS.ProcessEnv): {
|
|
28
|
+
sessionId: string;
|
|
29
|
+
dir: string;
|
|
30
|
+
} | undefined;
|
|
20
31
|
export interface StartedGateway {
|
|
21
32
|
gateway: AdtlGateway;
|
|
22
33
|
close(): Promise<void>;
|
package/dist/startup.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"startup.d.ts","sourceRoot":"","sources":["../src/startup.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAInE,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"startup.d.ts","sourceRoot":"","sources":["../src/startup.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAInE,OAAO,EAAsB,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAGnE,OAAO,EAAE,cAAc,EAAiB,MAAM,gBAAgB,CAAC;AAE/D,OAAO,EAKL,KAAK,aAAa,EAElB,KAAK,QAAQ,EACd,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,WAAW,EAAmB,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAIhF,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,aAAa,CAAC;CACxB;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAS3E;AAED,8GAA8G;AAC9G,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,CAqB/E;AAED,iDAAiD;AACjD,wBAAgB,sBAAsB,IAAI,MAAM,CAI/C;AAaD,wBAAsB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAO/F;AAED,0FAA0F;AAC1F,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,GAAG,EAAE,aAAa,GAAG,cAAc,GAAG,SAAS,CAiB1H;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAGpG;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,WAAW,CAAC;IACrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,YAAY,EACpB,IAAI,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IAAC,MAAM,CAAC,EAAE,aAAa,CAAA;CAAO,GAC7D,OAAO,CAAC,cAAc,CAAC,CAwEzB"}
|
package/dist/startup.js
CHANGED
|
@@ -9,6 +9,7 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
|
9
9
|
import { StdioClientTransport, getDefaultEnvironment } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
10
10
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
11
11
|
import { INTERCEPTORS_EXTENSION, LABEL_INTERCEPTOR, POLICY_INTERCEPTOR, parseConfigText } from "@adtl/interceptors";
|
|
12
|
+
import { defaultSessionsDir } from "@adtl/labels";
|
|
12
13
|
import { parse as parseYaml } from "yaml";
|
|
13
14
|
import { ZodError } from "zod";
|
|
14
15
|
import { ApprovalBroker } from "./approvals.js";
|
|
@@ -33,6 +34,11 @@ export async function loadGatewayConfig(path) {
|
|
|
33
34
|
catch (err) {
|
|
34
35
|
throw new GatewayConfigError(`Cannot read config file ${abs}: ${err.message}`);
|
|
35
36
|
}
|
|
37
|
+
return parseGatewayConfigText(text, abs);
|
|
38
|
+
}
|
|
39
|
+
/** Validate adtl.yaml text; `path` is where it lives (relative paths in it resolve against its directory). */
|
|
40
|
+
export function parseGatewayConfigText(text, path) {
|
|
41
|
+
const abs = resolve(path);
|
|
36
42
|
let labels;
|
|
37
43
|
try {
|
|
38
44
|
labels = parseConfigText(text, abs);
|
|
@@ -101,6 +107,15 @@ export function createApprovals(cfg, env, log) {
|
|
|
101
107
|
log.info(`approvals: ${notifiers.map((n) => n.name).join(", ")} (timeout ${a.timeoutSeconds}s)`);
|
|
102
108
|
return new ApprovalBroker({ notifiers, timeoutMs: a.timeoutSeconds * 1000, log });
|
|
103
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Under Claude Code, which passes `CLAUDE_CODE_SESSION_ID` to stdio MCP
|
|
112
|
+
* servers, the gateway uses that id and keeps session labels in files, so the
|
|
113
|
+
* Claude Code hooks (`adtl-gateway hook`) add to the same session label.
|
|
114
|
+
*/
|
|
115
|
+
export function sharedSession(env) {
|
|
116
|
+
const sessionId = env.CLAUDE_CODE_SESSION_ID?.trim();
|
|
117
|
+
return sessionId ? { sessionId, dir: defaultSessionsDir(env) } : undefined;
|
|
118
|
+
}
|
|
104
119
|
export async function startGateway(loaded, opts = {}) {
|
|
105
120
|
const env = opts.env ?? process.env;
|
|
106
121
|
const baseDir = dirname(loaded.path);
|
|
@@ -109,9 +124,15 @@ export async function startGateway(loaded, opts = {}) {
|
|
|
109
124
|
const closeAll = () => Promise.allSettled(clients.map((c) => c.close())).then(() => undefined);
|
|
110
125
|
try {
|
|
111
126
|
// 1. Interceptor server (out of process, per the Interceptors WG).
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
127
|
+
const shared = sharedSession(env);
|
|
128
|
+
const custom = loaded.gateway.interceptors && expandUpstream("interceptors", loaded.gateway.interceptors, env);
|
|
129
|
+
const icfg = custom
|
|
130
|
+
? { ...custom, env: { ...custom.env, ...(shared ? { ADTL_SESSIONS_DIR: shared.dir } : {}) } }
|
|
131
|
+
: {
|
|
132
|
+
command: process.execPath,
|
|
133
|
+
args: [bundledInterceptorsCli(), "--config", loaded.path, ...(shared ? ["--sessions-dir", shared.dir] : [])],
|
|
134
|
+
env: {},
|
|
135
|
+
};
|
|
115
136
|
const interceptors = await connectUpstream("interceptors", icfg, baseDir);
|
|
116
137
|
clients.push(interceptors);
|
|
117
138
|
if (!interceptors.getServerCapabilities()?.extensions?.[INTERCEPTORS_EXTENSION]) {
|
|
@@ -127,6 +148,8 @@ export async function startGateway(loaded, opts = {}) {
|
|
|
127
148
|
const audit = loaded.gateway.mode === "audit";
|
|
128
149
|
const invoker = new InterceptorInvoker(bound, loaded.gateway.interceptorTimeoutMs, audit ? "audit" : undefined);
|
|
129
150
|
log.info(`interceptors: ${names.join(", ")}`);
|
|
151
|
+
if (shared)
|
|
152
|
+
log.info(`sharing session labels with Claude Code hooks (${shared.dir})`);
|
|
130
153
|
if (audit)
|
|
131
154
|
log.warn("audit mode: calls are logged but never stopped or changed");
|
|
132
155
|
// 2. Upstream servers. One failing upstream does not stop the others.
|
|
@@ -152,6 +175,7 @@ export async function startGateway(loaded, opts = {}) {
|
|
|
152
175
|
const auditLog = loaded.gateway.audit ? new JsonlAuditLog(resolve(baseDir, loaded.gateway.audit)) : undefined;
|
|
153
176
|
const approvals = createApprovals(loaded.gateway, env, log);
|
|
154
177
|
const gateway = new AdtlGateway({
|
|
178
|
+
...(shared ? { sessionId: shared.sessionId } : {}),
|
|
155
179
|
upstreams,
|
|
156
180
|
invoker,
|
|
157
181
|
...(auditLog ? { audit: auditLog } : {}),
|
package/dist/startup.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"startup.js","sourceRoot":"","sources":["../src/startup.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,2CAA2C,CAAC;AACxG,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"startup.js","sourceRoot":"","sources":["../src/startup.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adtl/gateway",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Experimental: MCP gateway that labels every tool result and blocks risky tool calls using ADTL trust labels",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"bin": {
|
|
27
|
-
"adtl-gateway": "
|
|
27
|
+
"adtl-gateway": "dist/cli.js"
|
|
28
28
|
},
|
|
29
29
|
"files": [
|
|
30
30
|
"dist",
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"url": "https://github.com/probita-ai/adtl/issues"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@adtl/interceptors": "0.3.
|
|
50
|
-
"@adtl/labels": "0.3.
|
|
49
|
+
"@adtl/interceptors": "0.3.2",
|
|
50
|
+
"@adtl/labels": "0.3.2",
|
|
51
51
|
"@modelcontextprotocol/sdk": "1.31.0",
|
|
52
52
|
"nodemailer": "10.0.12",
|
|
53
53
|
"yaml": "2.9.1",
|
package/src/audit.ts
CHANGED
|
@@ -16,8 +16,10 @@ export interface AuditEntry {
|
|
|
16
16
|
tool?: string;
|
|
17
17
|
resource?: string;
|
|
18
18
|
prompt?: string;
|
|
19
|
-
/** Final outcome of the call. */
|
|
20
|
-
outcome: "forwarded" | "blocked" | "upstream_error";
|
|
19
|
+
/** Final outcome of the call. "asked": handed to Claude Code's permission prompt by the hook. */
|
|
20
|
+
outcome: "forwarded" | "blocked" | "asked" | "upstream_error";
|
|
21
|
+
/** Present when the entry was written by the Claude Code hook rather than the gateway. */
|
|
22
|
+
via?: "claude-code-hook";
|
|
21
23
|
/** The policy's decision. In audit mode a call can be forwarded with decision "ask" or "block". */
|
|
22
24
|
decision?: string;
|
|
23
25
|
/** Present when the gateway ran in audit mode: nothing was stopped or changed. */
|
package/src/cli.ts
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* servers through ADTL's interceptors.
|
|
5
5
|
*
|
|
6
6
|
* adtl-gateway --config adtl.yaml [--audit]
|
|
7
|
-
* adtl-gateway init --config adtl.yaml [--write]
|
|
7
|
+
* adtl-gateway init --config adtl.yaml [--from .mcp.json] [--write]
|
|
8
|
+
* adtl-gateway hook --config adtl.yaml [--gateway-server adtl]
|
|
8
9
|
*
|
|
9
10
|
* Point your MCP client (e.g. Claude Code's .mcp.json) at this command.
|
|
10
11
|
* Logs go to stderr; stdout carries MCP JSON-RPC only.
|
|
@@ -12,11 +13,13 @@
|
|
|
12
13
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
13
14
|
import { EnvExpansionError } from "./config.js";
|
|
14
15
|
import { GATEWAY_VERSION } from "./gateway.js";
|
|
16
|
+
import { runHook } from "./hooks/run.js";
|
|
15
17
|
import { runInit, type InitResult } from "./init/run.js";
|
|
16
18
|
import { GatewayConfigError, loadGatewayConfig, startGateway } from "./startup.js";
|
|
17
19
|
|
|
18
20
|
const USAGE = `Usage: adtl-gateway --config <adtl.yaml> [--audit]
|
|
19
|
-
adtl-gateway init --config <adtl.yaml> [--write]
|
|
21
|
+
adtl-gateway init --config <adtl.yaml> [--from <.mcp.json>] [--write]
|
|
22
|
+
adtl-gateway hook --config <adtl.yaml> [--gateway-server <name>]
|
|
20
23
|
|
|
21
24
|
Runs an MCP server over stdio that forwards tool calls to the upstream
|
|
22
25
|
servers in the config, checking and labeling each call with ADTL.
|
|
@@ -32,11 +35,30 @@ init:
|
|
|
32
35
|
for each tool the config doesn't cover yet (presets for GitHub,
|
|
33
36
|
filesystem and fetch; MCP tool hints otherwise). Existing entries are
|
|
34
37
|
never changed. Prints the updated config; review it before using it.
|
|
38
|
+
--from <file> First import the MCP servers of a client config (Claude
|
|
39
|
+
Code's .mcp.json, or any file with "mcpServers") into
|
|
40
|
+
gateway.upstreams, creating the config file if needed.
|
|
41
|
+
With --write, that file is changed to run the gateway
|
|
42
|
+
in place of the imported servers (original kept as .bak).
|
|
35
43
|
--write Update the config file in place (original kept as .bak).
|
|
44
|
+
|
|
45
|
+
hook:
|
|
46
|
+
The Claude Code hook for PreToolUse, PostToolUse and PostToolUseFailure.
|
|
47
|
+
Labels built-in tools (WebFetch, Read, Bash, ...) and MCP servers outside
|
|
48
|
+
the gateway, and runs the same policy on them, sharing the session label
|
|
49
|
+
with the gateway. Reads the hook event from stdin.
|
|
50
|
+
--gateway-server <name>
|
|
51
|
+
The gateway's name in Claude Code's MCP config; its
|
|
52
|
+
tools are checked by the gateway and skipped here.
|
|
53
|
+
Default: adtl.
|
|
54
|
+
|
|
36
55
|
-h, --help Show this help.`;
|
|
37
56
|
|
|
38
57
|
interface Args {
|
|
39
58
|
init?: boolean;
|
|
59
|
+
from?: string;
|
|
60
|
+
hook?: boolean;
|
|
61
|
+
gatewayServer?: string;
|
|
40
62
|
write?: boolean;
|
|
41
63
|
config?: string;
|
|
42
64
|
audit?: boolean;
|
|
@@ -49,12 +71,15 @@ function parseArgs(argv: string[]): Args {
|
|
|
49
71
|
for (let i = 0; i < argv.length; i++) {
|
|
50
72
|
const a = argv[i];
|
|
51
73
|
if (i === 0 && a === "init") out.init = true;
|
|
74
|
+
else if (i === 0 && a === "hook") out.hook = true;
|
|
75
|
+
else if (a === "--gateway-server" && out.hook) out.gatewayServer = argv[++i];
|
|
52
76
|
else if (a === "-h" || a === "--help") out.help = true;
|
|
53
77
|
else if (a === "-v" || a === "--version") out.version = true;
|
|
54
78
|
else if (a === "-c" || a === "--config") out.config = argv[++i];
|
|
55
79
|
else if (a?.startsWith("--config=")) out.config = a.slice("--config=".length);
|
|
56
80
|
else if (a === "--audit") out.audit = true;
|
|
57
81
|
else if (a === "--write" && out.init) out.write = true;
|
|
82
|
+
else if (a === "--from" && out.init) out.from = argv[++i];
|
|
58
83
|
else throw new GatewayConfigError(`Unknown argument: ${a}\n\n${USAGE}`);
|
|
59
84
|
}
|
|
60
85
|
return out;
|
|
@@ -66,7 +91,8 @@ async function main(): Promise<void> {
|
|
|
66
91
|
if (args.help) return void console.log(USAGE);
|
|
67
92
|
if (args.version) return void console.log(GATEWAY_VERSION);
|
|
68
93
|
if (!args.config) throw new GatewayConfigError(`Missing --config\n\n${USAGE}`);
|
|
69
|
-
if (args.init) return init(args.config, args.write === true);
|
|
94
|
+
if (args.init) return init(args.config, args.write === true, args.from);
|
|
95
|
+
if (args.hook) return hook(args.config, args.gatewayServer);
|
|
70
96
|
|
|
71
97
|
const loaded = await loadGatewayConfig(args.config);
|
|
72
98
|
if (args.audit) loaded.gateway.mode = "audit";
|
|
@@ -93,26 +119,54 @@ async function main(): Promise<void> {
|
|
|
93
119
|
}
|
|
94
120
|
|
|
95
121
|
function summarize(r: InitResult, path: string, write: boolean): string {
|
|
96
|
-
const lines =
|
|
122
|
+
const lines: string[] = [];
|
|
123
|
+
if (r.mcp) {
|
|
124
|
+
const m = r.mcp;
|
|
125
|
+
for (const i of m.imported) lines.push(` imported ${i.name}${i.id !== i.name ? ` as ${i.id}` : ""} from ${m.path}`);
|
|
126
|
+
for (const k of m.kept) lines.push(` ${k.name}: already in the config as ${k.id}, kept as it is`);
|
|
127
|
+
for (const s of m.skipped) lines.push(` ${s.name}: left in ${m.path} (${s.reason})`);
|
|
128
|
+
if (!m.imported.length && !m.kept.length) lines.push(` nothing to import from ${m.path}`);
|
|
129
|
+
}
|
|
130
|
+
if (r.created) lines.push(` ${write ? "created" : "would create"} ${path}`);
|
|
131
|
+
lines.push(...r.reports.map((s) => {
|
|
97
132
|
const parts = [`${s.added.length} tools added`];
|
|
98
133
|
if (s.kept.length) parts.push(`${s.kept.length} kept`);
|
|
99
134
|
if (s.addedDefaults) parts.push("defaults added");
|
|
100
135
|
if (s.stale.length) parts.push(`no longer offered: ${s.stale.join(", ")}`);
|
|
101
136
|
return ` ${s.id} (${s.preset ? `preset ${s.preset}` : "from tool hints, review"}): ${parts.join("; ")}`;
|
|
102
|
-
});
|
|
137
|
+
}));
|
|
103
138
|
for (const f of r.failed) lines.push(` ${f.id}: could not connect, skipped (${f.error})`);
|
|
104
|
-
const changed = r.reports.some((s) => s.added.length || s.addedDefaults);
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
139
|
+
const changed = r.created || r.mcp?.changed || r.reports.some((s) => s.added.length || s.addedDefaults);
|
|
140
|
+
let tail: string;
|
|
141
|
+
if (!changed) tail = "Nothing to add.";
|
|
142
|
+
else if (!write) tail = "Nothing written. Review the config above, then re-run with --write.";
|
|
143
|
+
else {
|
|
144
|
+
tail = r.created ? `Wrote ${path}.` : `Updated ${path} (original saved as ${path}.bak).`;
|
|
145
|
+
if (r.mcp?.changed) tail += ` ${r.mcp.path} now runs the gateway (original saved as ${r.mcp.path}.bak); restart your MCP client.`;
|
|
146
|
+
tail += ' Review every line marked "review".';
|
|
147
|
+
}
|
|
110
148
|
return `${lines.join("\n")}\n${tail}`;
|
|
111
149
|
}
|
|
112
150
|
|
|
113
|
-
async function
|
|
114
|
-
const
|
|
115
|
-
|
|
151
|
+
async function readStdin(): Promise<string> {
|
|
152
|
+
const chunks: Buffer[] = [];
|
|
153
|
+
for await (const c of process.stdin) chunks.push(c as Buffer);
|
|
154
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function hook(config: string, gatewayServer?: string): Promise<void> {
|
|
158
|
+
const out = await runHook({ configPath: config, stdin: await readStdin(), ...(gatewayServer ? { gatewayServer } : {}) });
|
|
159
|
+
if (out.stdout) process.stdout.write(`${JSON.stringify(out.stdout)}\n`);
|
|
160
|
+
if (out.stderr) process.stderr.write(`${out.stderr}\n`);
|
|
161
|
+
process.exitCode = out.exitCode;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function init(path: string, write: boolean, from?: string): Promise<void> {
|
|
165
|
+
const result = await runInit({ path, write, ...(from ? { from } : {}) });
|
|
166
|
+
if (!write) {
|
|
167
|
+
process.stdout.write(result.text);
|
|
168
|
+
if (result.mcp?.changed) process.stdout.write(`\n# ${result.mcp.path} with --write:\n${result.mcp.text.replace(/^/gm, "# ")}\n`);
|
|
169
|
+
}
|
|
116
170
|
console.error(`[adtl-gateway] init\n${summarize(result, path, write)}`);
|
|
117
171
|
if (result.failed.length && !result.reports.length) process.exitCode = 1;
|
|
118
172
|
}
|
|
@@ -120,5 +174,6 @@ async function init(path: string, write: boolean): Promise<void> {
|
|
|
120
174
|
main().catch((err: unknown) => {
|
|
121
175
|
const known = err instanceof GatewayConfigError || err instanceof EnvExpansionError;
|
|
122
176
|
console.error(known ? `[adtl-gateway] ${(err as Error).message}` : err);
|
|
123
|
-
|
|
177
|
+
// For Claude Code hooks only exit code 2 stops the call; any other code lets it run.
|
|
178
|
+
process.exit(process.argv[2] === "hook" ? 2 : 1);
|
|
124
179
|
});
|
package/src/config.ts
CHANGED
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
* to: you@example.com
|
|
22
22
|
*
|
|
23
23
|
* `${VAR}` in command, args, env, cwd, url, headers and approval settings is
|
|
24
|
-
* replaced from the gateway's environment. A missing variable is a config error
|
|
24
|
+
* replaced from the gateway's environment. A missing variable is a config error,
|
|
25
|
+
* unless a default is given: `${VAR:-default}`.
|
|
25
26
|
*/
|
|
26
27
|
import { z } from "zod";
|
|
27
28
|
import { EMAIL_EVENTS } from "./email.js";
|
|
@@ -125,11 +126,16 @@ export class EnvExpansionError extends Error {
|
|
|
125
126
|
}
|
|
126
127
|
}
|
|
127
128
|
|
|
128
|
-
/**
|
|
129
|
+
/**
|
|
130
|
+
* Replace `${VAR}` with values from `env`, and `${VAR:-default}` with `default`
|
|
131
|
+
* when VAR is unset or empty (as Claude Code's .mcp.json does). `$${VAR}` keeps
|
|
132
|
+
* a literal `${VAR}`.
|
|
133
|
+
*/
|
|
129
134
|
export function expandEnv(value: string, env: NodeJS.ProcessEnv, where: string): string {
|
|
130
|
-
return value.replace(/(\$?)\$\{([A-Za-z_][A-Za-z0-9_]*)
|
|
135
|
+
return value.replace(/(\$?)\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\}/g, (match, escape: string, name: string, fallback?: string) => {
|
|
131
136
|
if (escape) return match.slice(1);
|
|
132
137
|
const v = env[name];
|
|
138
|
+
if (fallback !== undefined) return v === undefined || v === "" ? fallback : v;
|
|
133
139
|
if (v === undefined) throw new EnvExpansionError(name, where);
|
|
134
140
|
return v;
|
|
135
141
|
});
|
package/src/gateway.ts
CHANGED
|
@@ -51,7 +51,7 @@ import type { AuditEntry, AuditSink } from "./audit.js";
|
|
|
51
51
|
import { TOOL_SEPARATOR } from "./config.js";
|
|
52
52
|
import type { Blocked, ChainOutcome, InterceptorInvoker } from "./invoker.js";
|
|
53
53
|
|
|
54
|
-
export const GATEWAY_VERSION = "0.3.
|
|
54
|
+
export const GATEWAY_VERSION = "0.3.2";
|
|
55
55
|
|
|
56
56
|
/** `_meta` key on gateway-generated error results describing why a call was stopped. */
|
|
57
57
|
export const GATEWAY_DECISION_KEY = "adtl/gateway";
|