@scupit/mcp-ecosystem 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +273 -0
- package/dist/auth0/management-client.d.ts +6 -1
- package/dist/auth0/management-client.d.ts.map +1 -1
- package/dist/auth0/management-client.js +56 -20
- package/dist/auth0/management-client.js.map +1 -1
- package/dist/cli.js +9 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/add-scope.d.ts.map +1 -1
- package/dist/commands/add-scope.js +5 -1
- package/dist/commands/add-scope.js.map +1 -1
- package/dist/commands/env-root-validation.d.ts +3 -0
- package/dist/commands/env-root-validation.d.ts.map +1 -0
- package/dist/commands/env-root-validation.js +27 -0
- package/dist/commands/env-root-validation.js.map +1 -0
- package/dist/commands/generate-artifacts.d.ts +1 -1
- package/dist/commands/generate-artifacts.d.ts.map +1 -1
- package/dist/commands/generate-artifacts.js +49 -75
- package/dist/commands/generate-artifacts.js.map +1 -1
- package/dist/commands/grant-client.d.ts.map +1 -1
- package/dist/commands/grant-client.js +17 -2
- package/dist/commands/grant-client.js.map +1 -1
- package/dist/commands/reconcile-all.d.ts.map +1 -1
- package/dist/commands/reconcile-all.js +9 -0
- package/dist/commands/reconcile-all.js.map +1 -1
- package/dist/commands/reconcile-client.d.ts +3 -1
- package/dist/commands/reconcile-client.d.ts.map +1 -1
- package/dist/commands/reconcile-client.js +109 -17
- package/dist/commands/reconcile-client.js.map +1 -1
- package/dist/commands/reconcile-server.d.ts.map +1 -1
- package/dist/commands/reconcile-server.js +49 -20
- package/dist/commands/reconcile-server.js.map +1 -1
- package/dist/commands/validated-client-cache.d.ts +7 -0
- package/dist/commands/validated-client-cache.d.ts.map +1 -0
- package/dist/commands/validated-client-cache.js +50 -0
- package/dist/commands/validated-client-cache.js.map +1 -0
- package/dist/commands/verify-tenant.d.ts.map +1 -1
- package/dist/commands/verify-tenant.js +2 -5
- package/dist/commands/verify-tenant.js.map +1 -1
- package/dist/config/defaults.d.ts +19 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +49 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/env-policy.d.ts +7 -0
- package/dist/config/env-policy.d.ts.map +1 -0
- package/dist/config/env-policy.js +30 -0
- package/dist/config/env-policy.js.map +1 -0
- package/dist/config/index.d.ts +3 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +3 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/loader.d.ts +15 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +95 -4
- package/dist/config/loader.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp-server/create-server.d.ts +17 -24
- package/dist/mcp-server/create-server.d.ts.map +1 -1
- package/dist/mcp-server/create-server.js +157 -58
- package/dist/mcp-server/create-server.js.map +1 -1
- package/dist/mcp-server/http-app.d.ts +24 -0
- package/dist/mcp-server/http-app.d.ts.map +1 -0
- package/dist/mcp-server/http-app.js +66 -0
- package/dist/mcp-server/http-app.js.map +1 -0
- package/dist/mcp-server/index.d.ts +4 -2
- package/dist/mcp-server/index.d.ts.map +1 -1
- package/dist/mcp-server/index.js +1 -1
- package/dist/mcp-server/index.js.map +1 -1
- package/dist/mcp-server/mcp-configuration.d.ts +35 -0
- package/dist/mcp-server/mcp-configuration.d.ts.map +1 -0
- package/dist/mcp-server/mcp-configuration.js +2 -0
- package/dist/mcp-server/mcp-configuration.js.map +1 -0
- package/dist/mcp-server/stdio-mcp.d.ts +17 -0
- package/dist/mcp-server/stdio-mcp.d.ts.map +1 -0
- package/dist/mcp-server/stdio-mcp.js +28 -0
- package/dist/mcp-server/stdio-mcp.js.map +1 -0
- package/dist/mcp-server/streamable-http-stateful-mcp.d.ts +22 -0
- package/dist/mcp-server/streamable-http-stateful-mcp.d.ts.map +1 -0
- package/dist/mcp-server/streamable-http-stateful-mcp.js +105 -0
- package/dist/mcp-server/streamable-http-stateful-mcp.js.map +1 -0
- package/dist/mcp-server/streamable-http-stateless-mcp.d.ts +20 -0
- package/dist/mcp-server/streamable-http-stateless-mcp.d.ts.map +1 -0
- package/dist/mcp-server/streamable-http-stateless-mcp.js +58 -0
- package/dist/mcp-server/streamable-http-stateless-mcp.js.map +1 -0
- package/dist/mcp-server/transport-config.d.ts +80 -0
- package/dist/mcp-server/transport-config.d.ts.map +1 -0
- package/dist/mcp-server/transport-config.js +2 -0
- package/dist/mcp-server/transport-config.js.map +1 -0
- package/dist/types/auth0-responses.d.ts +11 -0
- package/dist/types/auth0-responses.d.ts.map +1 -1
- package/dist/types/client-config.d.ts +6 -24
- package/dist/types/client-config.d.ts.map +1 -1
- package/dist/types/client-config.js +4 -7
- package/dist/types/client-config.js.map +1 -1
- package/dist/types/ecosystem-config.d.ts +211 -70
- package/dist/types/ecosystem-config.d.ts.map +1 -1
- package/dist/types/ecosystem-config.js +33 -21
- package/dist/types/ecosystem-config.js.map +1 -1
- package/dist/types/index.d.ts +3 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/server-config.js +1 -1
- package/dist/types/server-config.js.map +1 -1
- package/dist/utils/context.d.ts +6 -5
- package/dist/utils/context.d.ts.map +1 -1
- package/dist/utils/context.js +43 -7
- package/dist/utils/context.js.map +1 -1
- package/dist/utils/env-manager.d.ts +113 -0
- package/dist/utils/env-manager.d.ts.map +1 -0
- package/dist/utils/env-manager.js +509 -0
- package/dist/utils/env-manager.js.map +1 -0
- package/dist/utils/env-writer.d.ts +23 -0
- package/dist/utils/env-writer.d.ts.map +1 -0
- package/dist/utils/env-writer.js +131 -0
- package/dist/utils/env-writer.js.map +1 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/package.json +8 -6
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { isManagedClientCredentialEnvKey } from "../config/index.js";
|
|
3
|
+
export function assertNoManagedClientCredentialsOutsideManagedBlock(ctx) {
|
|
4
|
+
const invalidAssignments = ctx.envManager.getOuterAssignments(isManagedClientCredentialEnvKey);
|
|
5
|
+
if (invalidAssignments.length === 0) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
const envPath = join(ctx.rootDir, ".env");
|
|
9
|
+
const entries = invalidAssignments.map((assignment) => ` ${maskSecretAssignment(assignment.rawLine)}`);
|
|
10
|
+
throw new Error(`Invalid tool-managed client credentials in root .env at ${envPath}\n\n` +
|
|
11
|
+
` These variables are managed by mcp-ecosystem and must appear only inside\n` +
|
|
12
|
+
` the auto-generated block marked by:\n` +
|
|
13
|
+
` # <automatically-generated>\n` +
|
|
14
|
+
` # </automatically-generated>\n\n` +
|
|
15
|
+
` Remove these user-authored assignments from outside the managed block:\n` +
|
|
16
|
+
`${entries.join("\n")}\n\n` +
|
|
17
|
+
` Then re-run reconcile-client or reconcile-all so the CLI can regenerate\n` +
|
|
18
|
+
` the managed entries safely.`);
|
|
19
|
+
}
|
|
20
|
+
function maskSecretAssignment(line) {
|
|
21
|
+
const eqIdx = line.indexOf("=");
|
|
22
|
+
if (eqIdx === -1)
|
|
23
|
+
return line;
|
|
24
|
+
const key = line.slice(0, eqIdx).trim();
|
|
25
|
+
return key.includes("_SECRET") ? `${key}=********` : line;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=env-root-validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-root-validation.js","sourceRoot":"","sources":["../../src/commands/env-root-validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,+BAA+B,EAAE,MAAM,oBAAoB,CAAC;AAGrE,MAAM,UAAU,mDAAmD,CACjE,GAAmD;IAEnD,MAAM,kBAAkB,GAAG,GAAG,CAAC,UAAU,CAAC,mBAAmB,CAC3D,+BAA+B,CAChC,CAAC;IAEF,IAAI,kBAAkB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpC,OAAO;IACT,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1C,MAAM,OAAO,GAAG,kBAAkB,CAAC,GAAG,CACpC,CAAC,UAAU,EAAE,EAAE,CAAC,OAAO,oBAAoB,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAClE,CAAC;IAEF,MAAM,IAAI,KAAK,CACb,2DAA2D,OAAO,MAAM;QACtE,8EAA8E;QAC9E,yCAAyC;QACzC,mCAAmC;QACnC,sCAAsC;QACtC,4EAA4E;QAC5E,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM;QAC3B,6EAA6E;QAC7E,+BAA+B,CAClC,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAAC,IAAY;IACxC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,KAAK,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IACxC,OAAO,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5D,CAAC"}
|
|
@@ -3,7 +3,7 @@ export interface GenerateArtifactsResult {
|
|
|
3
3
|
filesWritten: string[];
|
|
4
4
|
}
|
|
5
5
|
/**
|
|
6
|
-
* Generates or refreshes the .env.example
|
|
6
|
+
* Generates or refreshes the .env.example file.
|
|
7
7
|
* Never writes live secrets.
|
|
8
8
|
*/
|
|
9
9
|
export declare function generateArtifacts(ctx: CommandContext): Promise<GenerateArtifactsResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-artifacts.d.ts","sourceRoot":"","sources":["../../src/commands/generate-artifacts.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"generate-artifacts.d.ts","sourceRoot":"","sources":["../../src/commands/generate-artifacts.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAQxD,MAAM,WAAW,uBAAuB;IACtC,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,uBAAuB,CAAC,CA4ClC"}
|
|
@@ -1,91 +1,65 @@
|
|
|
1
|
-
import { writeFile, mkdir } from "node:fs/promises";
|
|
2
1
|
import { join } from "node:path";
|
|
3
|
-
import { logger } from "../utils/index.js";
|
|
4
|
-
import {
|
|
2
|
+
import { logger, ManagedEnvFile } from "../utils/index.js";
|
|
3
|
+
import { ECOSYSTEM_ENV, AUTH0_ENV, getEnvPlaceholder, } from "../config/index.js";
|
|
5
4
|
/**
|
|
6
|
-
* Generates or refreshes the .env.example
|
|
5
|
+
* Generates or refreshes the .env.example file.
|
|
7
6
|
* Never writes live secrets.
|
|
8
7
|
*/
|
|
9
8
|
export async function generateArtifacts(ctx) {
|
|
10
|
-
const {
|
|
11
|
-
const ecosystem = config.ecosystem;
|
|
9
|
+
const { rootDir, dryRun } = ctx;
|
|
12
10
|
const written = [];
|
|
13
|
-
// ── .env.example ──
|
|
14
11
|
const envLines = [
|
|
15
|
-
"#
|
|
16
|
-
`${
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
"# Ecosystem base domain (required)",
|
|
13
|
+
`${ECOSYSTEM_ENV.baseDomain}=${getEnvPlaceholder(ECOSYSTEM_ENV.baseDomain) ?? "example.com"}`,
|
|
14
|
+
"",
|
|
15
|
+
"# Auth0 tenant and Management API credentials (required for provisioning)",
|
|
16
|
+
`${AUTH0_ENV.tenantDomain}=${getEnvPlaceholder(AUTH0_ENV.tenantDomain) ?? "your-tenant.auth0.com"}`,
|
|
17
|
+
`${AUTH0_ENV.clientId}=${getEnvPlaceholder(AUTH0_ENV.clientId) ?? "__REQUIRED__"}`,
|
|
18
|
+
`${AUTH0_ENV.clientSecret}=${getEnvPlaceholder(AUTH0_ENV.clientSecret) ?? "__REQUIRED__"}`,
|
|
19
|
+
"",
|
|
20
|
+
"# Client IDs and secrets are automatically written to .env by",
|
|
21
|
+
"# reconcile-client and reconcile-all. Do not set them manually.",
|
|
19
22
|
"",
|
|
20
23
|
];
|
|
21
|
-
for (const [key, client] of config.clientConfigs) {
|
|
22
|
-
const envPrefix = `AUTH0_${key.toUpperCase().replace(/-/g, "_")}`;
|
|
23
|
-
envLines.push(`# Client: ${client.display_name}`);
|
|
24
|
-
envLines.push(`${envPrefix}_CLIENT_ID=__GENERATED_OR_EXISTING__`);
|
|
25
|
-
const isPublic = client.profile === "native_interactive" ||
|
|
26
|
-
client.profile === "spa_interactive";
|
|
27
|
-
if (client.credentials?.client_secret_env) {
|
|
28
|
-
envLines.push(`${client.credentials.client_secret_env}=__FILL_SECRET__`);
|
|
29
|
-
}
|
|
30
|
-
else if (!isPublic) {
|
|
31
|
-
envLines.push(`${envPrefix}_CLIENT_SECRET=__GENERATED__`);
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
envLines.push(`# ${envPrefix}_CLIENT_SECRET=__ONLY_IF_CONFIDENTIAL__`);
|
|
35
|
-
}
|
|
36
|
-
envLines.push("");
|
|
37
|
-
}
|
|
38
|
-
for (const [slug, server] of config.serverConfigs) {
|
|
39
|
-
const identifier = deriveCanonicalResourceUri(ecosystem, slug);
|
|
40
|
-
const envPrefix = `AUTH0_${slug.toUpperCase().replace(/-/g, "_")}_MCP`;
|
|
41
|
-
envLines.push(`# Server: ${server.name}`);
|
|
42
|
-
envLines.push(`${envPrefix}_AUDIENCE=${identifier}`);
|
|
43
|
-
envLines.push("");
|
|
44
|
-
}
|
|
45
24
|
const envExamplePath = join(rootDir, ".env.example");
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
25
|
+
const envExample = await ManagedEnvFile.load(envExamplePath, {
|
|
26
|
+
displayName: ".env.example",
|
|
27
|
+
blockHeaderLines: [
|
|
28
|
+
"# Managed by @scupit/mcp-ecosystem -- placeholders only.",
|
|
29
|
+
"# Add your own notes or extra example variables outside this block.",
|
|
30
|
+
],
|
|
31
|
+
});
|
|
32
|
+
if (!envExample.hasManagedBlock && isLegacyGeneratedEnvExample(envExample.getOuterContent())) {
|
|
33
|
+
envExample.replaceOuterContent("");
|
|
50
34
|
}
|
|
51
|
-
|
|
52
|
-
|
|
35
|
+
envExample.setManagedLines(envLines);
|
|
36
|
+
const changed = await envExample.flush(dryRun, {
|
|
37
|
+
dryRunLabel: "[DRY RUN] Would update .env.example managed block:",
|
|
38
|
+
successMessage: `Generated: ${envExamplePath}`,
|
|
39
|
+
});
|
|
40
|
+
if (changed && !dryRun) {
|
|
41
|
+
written.push(envExamplePath);
|
|
53
42
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const hostname = deriveHostname(ecosystem, slug);
|
|
57
|
-
const resourceUri = deriveCanonicalResourceUri(ecosystem, slug);
|
|
58
|
-
const mcpEndpoint = deriveMcpEndpoint(ecosystem, slug);
|
|
59
|
-
const metadataUrl = deriveProtectedResourceMetadataUrl(ecosystem, slug);
|
|
60
|
-
const scopes = resolveScopes(ecosystem, server);
|
|
61
|
-
const runtimeConfig = {
|
|
62
|
-
server: {
|
|
63
|
-
name: server.name,
|
|
64
|
-
slug: server.slug,
|
|
65
|
-
hostname,
|
|
66
|
-
resource_uri: resourceUri,
|
|
67
|
-
mcp_endpoint: mcpEndpoint,
|
|
68
|
-
},
|
|
69
|
-
auth: {
|
|
70
|
-
issuer: `https://${ecosystem.auth0.tenant_domain}/`,
|
|
71
|
-
audience: resourceUri,
|
|
72
|
-
jwks_uri: `https://${ecosystem.auth0.tenant_domain}/.well-known/jwks.json`,
|
|
73
|
-
protected_resource_metadata_url: metadataUrl,
|
|
74
|
-
},
|
|
75
|
-
scopes,
|
|
76
|
-
};
|
|
77
|
-
const serverDir = join(rootDir, "mcps", slug);
|
|
78
|
-
await mkdir(serverDir, { recursive: true });
|
|
79
|
-
const runtimeConfigPath = join(serverDir, "runtime-config.generated.json");
|
|
80
|
-
if (!dryRun) {
|
|
81
|
-
await writeFile(runtimeConfigPath, JSON.stringify(runtimeConfig, null, 2) + "\n", "utf-8");
|
|
82
|
-
written.push(runtimeConfigPath);
|
|
83
|
-
logger.success(`Generated: ${runtimeConfigPath}`);
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
logger.info(`[DRY RUN] Would generate: ${runtimeConfigPath}`);
|
|
87
|
-
}
|
|
43
|
+
else if (!changed) {
|
|
44
|
+
logger.info(`No changes needed: ${envExamplePath}`);
|
|
88
45
|
}
|
|
89
46
|
return { filesWritten: written };
|
|
90
47
|
}
|
|
48
|
+
function isLegacyGeneratedEnvExample(content) {
|
|
49
|
+
const lines = content.split(/\r?\n/);
|
|
50
|
+
while (lines.length > 0 && lines[lines.length - 1] === "") {
|
|
51
|
+
lines.pop();
|
|
52
|
+
}
|
|
53
|
+
return (lines.length === 10 &&
|
|
54
|
+
lines[0] === "# Ecosystem base domain (required)" &&
|
|
55
|
+
/^ECOSYSTEM_BASE_DOMAIN=/.test(lines[1] ?? "") &&
|
|
56
|
+
lines[2] === "" &&
|
|
57
|
+
lines[3] === "# Auth0 tenant and Management API credentials (required for provisioning)" &&
|
|
58
|
+
/^AUTH0_TENANT_DOMAIN=/.test(lines[4] ?? "") &&
|
|
59
|
+
/^AUTH0_MGMT_CLIENT_ID=/.test(lines[5] ?? "") &&
|
|
60
|
+
/^AUTH0_MGMT_CLIENT_SECRET=/.test(lines[6] ?? "") &&
|
|
61
|
+
lines[7] === "" &&
|
|
62
|
+
lines[8] === "# Client IDs and secrets are automatically written to .env by" &&
|
|
63
|
+
lines[9] === "# reconcile-client and reconcile-all. Do not set them manually.");
|
|
64
|
+
}
|
|
91
65
|
//# sourceMappingURL=generate-artifacts.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-artifacts.js","sourceRoot":"","sources":["../../src/commands/generate-artifacts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"generate-artifacts.js","sourceRoot":"","sources":["../../src/commands/generate-artifacts.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grant-client.d.ts","sourceRoot":"","sources":["../../src/commands/grant-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"grant-client.d.ts","sourceRoot":"","sources":["../../src/commands/grant-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAYxD,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAAC;IACxD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,WAAW,CAC/B,GAAG,EAAE,cAAc,EACnB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,iBAAiB,CAAC,CA0J5B"}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { logger } from "../utils/index.js";
|
|
2
|
-
import { deriveCanonicalResourceUri, resolveScopes } from "../config/index.js";
|
|
2
|
+
import { assertRequiredEcosystemEnv, deriveCanonicalResourceUri, resolveClientAccessPolicy, resolveScopes, } from "../config/index.js";
|
|
3
3
|
import { reconcileClient } from "./reconcile-client.js";
|
|
4
|
+
import { assertNoManagedClientCredentialsOutsideManagedBlock } from "./env-root-validation.js";
|
|
5
|
+
import { loadValidatedCachedClientApplication } from "./validated-client-cache.js";
|
|
4
6
|
export async function grantClient(ctx, serverSlug, clientKey, scopes) {
|
|
5
7
|
const { config, auth0, dryRun } = ctx;
|
|
8
|
+
assertNoManagedClientCredentialsOutsideManagedBlock(ctx);
|
|
6
9
|
const serverConfig = config.serverConfigs.get(serverSlug);
|
|
7
10
|
if (!serverConfig) {
|
|
8
11
|
throw new Error(`Unknown server slug: "${serverSlug}"`);
|
|
@@ -11,13 +14,25 @@ export async function grantClient(ctx, serverSlug, clientKey, scopes) {
|
|
|
11
14
|
if (!clientConfig) {
|
|
12
15
|
throw new Error(`Unknown client key: "${clientKey}"`);
|
|
13
16
|
}
|
|
17
|
+
assertRequiredEcosystemEnv(config.ecosystem, {
|
|
18
|
+
context: `grant access to server "${serverSlug}"`,
|
|
19
|
+
requireBaseDomain: true,
|
|
20
|
+
});
|
|
14
21
|
const audience = deriveCanonicalResourceUri(config.ecosystem, serverConfig.slug);
|
|
15
22
|
const grantScopes = scopes && scopes.length > 0
|
|
16
23
|
? scopes
|
|
17
24
|
: resolveScopes(config.ecosystem, serverConfig);
|
|
18
25
|
const profileDef = config.ecosystem.defaults.client_profiles?.[clientConfig.profile];
|
|
19
26
|
const subjectType = profileDef?.access_mode === "machine" ? "client" : "user";
|
|
20
|
-
|
|
27
|
+
if (subjectType === "client" &&
|
|
28
|
+
resolveClientAccessPolicy(config.ecosystem, serverConfig) === "deny_all") {
|
|
29
|
+
throw new Error(`Server "${serverSlug}" has effective client access policy deny_all.\n\n` +
|
|
30
|
+
` Machine-to-machine grants are disabled for this server.\n` +
|
|
31
|
+
` Set access_policy.client or defaults.api.client_access_policy to "require_client_grant"\n` +
|
|
32
|
+
` before creating a client grant for "${clientKey}".`);
|
|
33
|
+
}
|
|
34
|
+
let clientId = (await loadValidatedCachedClientApplication(ctx, clientKey))
|
|
35
|
+
?.client_id;
|
|
21
36
|
if (!clientId) {
|
|
22
37
|
logger.info(`Ensuring client "${clientKey}" exists...`);
|
|
23
38
|
const result = await reconcileClient(ctx, clientKey);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grant-client.js","sourceRoot":"","sources":["../../src/commands/grant-client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,
|
|
1
|
+
{"version":3,"file":"grant-client.js","sourceRoot":"","sources":["../../src/commands/grant-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reconcile-all.d.ts","sourceRoot":"","sources":["../../src/commands/reconcile-all.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"reconcile-all.d.ts","sourceRoot":"","sources":["../../src/commands/reconcile-all.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAIxD,OAAO,EAAmB,KAAK,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACpF,OAAO,EAAmB,KAAK,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAGpF,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,qBAAqB,EAAE,CAAC;IACjC,OAAO,EAAE,qBAAqB,EAAE,CAAC;CAClC;AAED,wBAAsB,YAAY,CAChC,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,kBAAkB,CAAC,CA8E7B"}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { logger } from "../utils/index.js";
|
|
2
|
+
import { assertRequiredEcosystemEnv } from "../config/index.js";
|
|
2
3
|
import { verifyTenant } from "./verify-tenant.js";
|
|
3
4
|
import { reconcileClient } from "./reconcile-client.js";
|
|
4
5
|
import { reconcileServer } from "./reconcile-server.js";
|
|
6
|
+
import { assertNoManagedClientCredentialsOutsideManagedBlock } from "./env-root-validation.js";
|
|
5
7
|
export async function reconcileAll(ctx) {
|
|
6
8
|
const { config } = ctx;
|
|
9
|
+
assertNoManagedClientCredentialsOutsideManagedBlock(ctx);
|
|
7
10
|
logger.info("=== Full Ecosystem Reconciliation ===");
|
|
8
11
|
logger.blank();
|
|
9
12
|
// Phase 1: Verify tenant
|
|
@@ -28,6 +31,12 @@ export async function reconcileAll(ctx) {
|
|
|
28
31
|
}
|
|
29
32
|
logger.info("=== Phase 3: Reconcile MCP Servers ===");
|
|
30
33
|
logger.blank();
|
|
34
|
+
if (config.serverConfigs.size > 0) {
|
|
35
|
+
assertRequiredEcosystemEnv(config.ecosystem, {
|
|
36
|
+
context: "reconcile MCP servers",
|
|
37
|
+
requireBaseDomain: true,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
31
40
|
const serverResults = [];
|
|
32
41
|
for (const slug of config.serverConfigs.keys()) {
|
|
33
42
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reconcile-all.js","sourceRoot":"","sources":["../../src/commands/reconcile-all.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,eAAe,EAA8B,MAAM,uBAAuB,CAAC;AACpF,OAAO,EAAE,eAAe,EAA8B,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"reconcile-all.js","sourceRoot":"","sources":["../../src/commands/reconcile-all.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { CommandContext } from "../utils/index.js";
|
|
2
|
+
export type ClientSecretStatus = "not_applicable" | "dry_run" | "written" | "matched_existing_managed" | "skipped_existing_managed";
|
|
2
3
|
export interface ReconcileClientResult {
|
|
3
4
|
clientKey: string;
|
|
4
5
|
clientId: string;
|
|
5
6
|
action: "created" | "reused" | "patched" | "dry_run";
|
|
6
7
|
applicationType: string;
|
|
7
8
|
tokenEndpointAuthMethod: string;
|
|
8
|
-
|
|
9
|
+
secretWritten: boolean;
|
|
10
|
+
secretStatus: ClientSecretStatus;
|
|
9
11
|
}
|
|
10
12
|
export declare function reconcileClient(ctx: CommandContext, clientKey: string): Promise<ReconcileClientResult>;
|
|
11
13
|
//# sourceMappingURL=reconcile-client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reconcile-client.d.ts","sourceRoot":"","sources":["../../src/commands/reconcile-client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"reconcile-client.d.ts","sourceRoot":"","sources":["../../src/commands/reconcile-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAiBxD,MAAM,MAAM,kBAAkB,GAC1B,gBAAgB,GAChB,SAAS,GACT,SAAS,GACT,0BAA0B,GAC1B,0BAA0B,CAAC;AAE/B,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;IACrD,eAAe,EAAE,MAAM,CAAC;IACxB,uBAAuB,EAAE,MAAM,CAAC;IAChC,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,kBAAkB,CAAC;CAClC;AAcD,wBAAsB,eAAe,CACnC,GAAG,EAAE,cAAc,EACnB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,qBAAqB,CAAC,CA0QhC"}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { logger, clientIdEnvVar, clientSecretEnvVar } from "../utils/index.js";
|
|
3
|
+
import { assertNoManagedClientCredentialsOutsideManagedBlock } from "./env-root-validation.js";
|
|
4
|
+
import { expectedManagedClientMetadata, loadValidatedCachedClientApplication, MANAGED_BY, } from "./validated-client-cache.js";
|
|
2
5
|
export async function reconcileClient(ctx, clientKey) {
|
|
3
|
-
const { config, auth0, dryRun } = ctx;
|
|
6
|
+
const { config, auth0, envManager, dryRun } = ctx;
|
|
7
|
+
assertNoManagedClientCredentialsOutsideManagedBlock(ctx);
|
|
4
8
|
const clientConfig = config.clientConfigs.get(clientKey);
|
|
5
9
|
if (!clientConfig) {
|
|
6
10
|
throw new Error(`Unknown client key: "${clientKey}"`);
|
|
@@ -13,51 +17,77 @@ export async function reconcileClient(ctx, clientKey) {
|
|
|
13
17
|
logger.debug(` Profile: ${clientConfig.profile}`);
|
|
14
18
|
logger.debug(` App type: ${settings.appType}`);
|
|
15
19
|
logger.debug(` Auth method: ${settings.tokenEndpointAuthMethod}`);
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
const idEnvKey = clientIdEnvVar(clientKey);
|
|
21
|
+
const expectedMetadata = expectedManagedClientMetadata(config.ecosystem, clientConfig);
|
|
22
|
+
const existing = await loadValidatedCachedClientApplication(ctx, clientKey);
|
|
23
|
+
if (existing) {
|
|
24
|
+
logger.info(` Using existing client_id from ${idEnvKey}: ${existing.client_id}`);
|
|
19
25
|
const reuse = checkCompatibility(existing, settings);
|
|
20
26
|
if (!reuse.compatible) {
|
|
21
27
|
logger.warn(` Existing application is NOT fully compatible: ${reuse.reason}`);
|
|
22
|
-
const policy = descriptor
|
|
23
|
-
if (policy === "patch_if_safe"
|
|
28
|
+
const policy = resolveReusePolicy(clientConfig, descriptor);
|
|
29
|
+
if (policy === "patch_if_safe") {
|
|
30
|
+
if (dryRun) {
|
|
31
|
+
logger.info(" [DRY RUN] Would patch existing application.");
|
|
32
|
+
return {
|
|
33
|
+
clientKey,
|
|
34
|
+
clientId: existing.client_id,
|
|
35
|
+
action: "dry_run",
|
|
36
|
+
applicationType: settings.appType,
|
|
37
|
+
tokenEndpointAuthMethod: settings.tokenEndpointAuthMethod,
|
|
38
|
+
secretWritten: false,
|
|
39
|
+
secretStatus: "not_applicable",
|
|
40
|
+
};
|
|
41
|
+
}
|
|
24
42
|
logger.info(" Attempting safe patch...");
|
|
25
43
|
await patchApplication(auth0, existing, settings, config.ecosystem, clientConfig);
|
|
26
44
|
logger.success(" Application patched.");
|
|
45
|
+
assertManagedClientEnvWrite(envManager.set(idEnvKey, existing.client_id, {
|
|
46
|
+
rejectOuterCollision: true,
|
|
47
|
+
}), ctx.rootDir);
|
|
27
48
|
return {
|
|
28
49
|
clientKey,
|
|
29
50
|
clientId: existing.client_id,
|
|
30
51
|
action: "patched",
|
|
31
52
|
applicationType: settings.appType,
|
|
32
53
|
tokenEndpointAuthMethod: settings.tokenEndpointAuthMethod,
|
|
33
|
-
|
|
54
|
+
secretWritten: false,
|
|
55
|
+
secretStatus: "not_applicable",
|
|
34
56
|
};
|
|
35
57
|
}
|
|
36
58
|
logger.warn(" Reusing as-is (policy does not allow patching).");
|
|
37
59
|
}
|
|
60
|
+
assertManagedClientEnvWrite(envManager.set(idEnvKey, existing.client_id, {
|
|
61
|
+
rejectOuterCollision: true,
|
|
62
|
+
}), ctx.rootDir);
|
|
38
63
|
return {
|
|
39
64
|
clientKey,
|
|
40
65
|
clientId: existing.client_id,
|
|
41
66
|
action: "reused",
|
|
42
67
|
applicationType: settings.appType,
|
|
43
68
|
tokenEndpointAuthMethod: settings.tokenEndpointAuthMethod,
|
|
44
|
-
|
|
69
|
+
secretWritten: false,
|
|
70
|
+
secretStatus: "not_applicable",
|
|
45
71
|
};
|
|
46
72
|
}
|
|
47
73
|
logger.info(" Searching for existing Auth0 application by metadata...");
|
|
48
|
-
const existingApp = await auth0.
|
|
74
|
+
const existingApp = await auth0.findApplicationByMetadataEntries(expectedMetadata);
|
|
49
75
|
if (existingApp) {
|
|
50
76
|
logger.info(` Found existing application: ${existingApp.name} (${existingApp.client_id})`);
|
|
51
77
|
const reuse = checkCompatibility(existingApp, settings);
|
|
52
78
|
if (reuse.compatible) {
|
|
53
79
|
logger.success(" Application is compatible. Reusing.");
|
|
80
|
+
assertManagedClientEnvWrite(envManager.set(idEnvKey, existingApp.client_id, {
|
|
81
|
+
rejectOuterCollision: true,
|
|
82
|
+
}), ctx.rootDir);
|
|
54
83
|
return {
|
|
55
84
|
clientKey,
|
|
56
85
|
clientId: existingApp.client_id,
|
|
57
86
|
action: "reused",
|
|
58
87
|
applicationType: settings.appType,
|
|
59
88
|
tokenEndpointAuthMethod: settings.tokenEndpointAuthMethod,
|
|
60
|
-
|
|
89
|
+
secretWritten: false,
|
|
90
|
+
secretStatus: "not_applicable",
|
|
61
91
|
};
|
|
62
92
|
}
|
|
63
93
|
logger.warn(` Existing application is not compatible: ${reuse.reason}`);
|
|
@@ -71,19 +101,24 @@ export async function reconcileClient(ctx, clientKey) {
|
|
|
71
101
|
action: "dry_run",
|
|
72
102
|
applicationType: settings.appType,
|
|
73
103
|
tokenEndpointAuthMethod: settings.tokenEndpointAuthMethod,
|
|
74
|
-
|
|
104
|
+
secretWritten: false,
|
|
105
|
+
secretStatus: "not_applicable",
|
|
75
106
|
};
|
|
76
107
|
}
|
|
77
108
|
logger.info(" Patching existing application...");
|
|
78
109
|
await patchApplication(auth0, existingApp, settings, config.ecosystem, clientConfig);
|
|
79
110
|
logger.success(" Application patched.");
|
|
111
|
+
assertManagedClientEnvWrite(envManager.set(idEnvKey, existingApp.client_id, {
|
|
112
|
+
rejectOuterCollision: true,
|
|
113
|
+
}), ctx.rootDir);
|
|
80
114
|
return {
|
|
81
115
|
clientKey,
|
|
82
116
|
clientId: existingApp.client_id,
|
|
83
117
|
action: "patched",
|
|
84
118
|
applicationType: settings.appType,
|
|
85
119
|
tokenEndpointAuthMethod: settings.tokenEndpointAuthMethod,
|
|
86
|
-
|
|
120
|
+
secretWritten: false,
|
|
121
|
+
secretStatus: "not_applicable",
|
|
87
122
|
};
|
|
88
123
|
}
|
|
89
124
|
if (policy === "share_if_exact_match") {
|
|
@@ -104,13 +139,15 @@ export async function reconcileClient(ctx, clientKey) {
|
|
|
104
139
|
action: "dry_run",
|
|
105
140
|
applicationType: settings.appType,
|
|
106
141
|
tokenEndpointAuthMethod: settings.tokenEndpointAuthMethod,
|
|
107
|
-
|
|
142
|
+
secretWritten: false,
|
|
143
|
+
secretStatus: isConfidential(settings) ? "dry_run" : "not_applicable",
|
|
108
144
|
};
|
|
109
145
|
}
|
|
110
146
|
logger.info(" Creating new Auth0 Application...");
|
|
111
147
|
const newApp = await auth0.createApplication({
|
|
112
148
|
name: clientConfig.display_name,
|
|
113
149
|
app_type: settings.appType,
|
|
150
|
+
oidc_conformant: true,
|
|
114
151
|
token_endpoint_auth_method: settings.tokenEndpointAuthMethod,
|
|
115
152
|
grant_types: settings.grantTypes,
|
|
116
153
|
callbacks: settings.callbackUrls.length > 0 ? settings.callbackUrls : undefined,
|
|
@@ -121,7 +158,7 @@ export async function reconcileClient(ctx, clientKey) {
|
|
|
121
158
|
client_key: clientKey,
|
|
122
159
|
descriptor: clientConfig.descriptor ?? "",
|
|
123
160
|
profile: clientConfig.profile,
|
|
124
|
-
managed_by:
|
|
161
|
+
managed_by: MANAGED_BY,
|
|
125
162
|
},
|
|
126
163
|
...(settings.useRefreshTokens && {
|
|
127
164
|
refresh_token: {
|
|
@@ -133,13 +170,45 @@ export async function reconcileClient(ctx, clientKey) {
|
|
|
133
170
|
}),
|
|
134
171
|
});
|
|
135
172
|
logger.success(` Application created: ${newApp.name} (${newApp.client_id})`);
|
|
173
|
+
assertManagedClientEnvWrite(envManager.set(idEnvKey, newApp.client_id, {
|
|
174
|
+
rejectOuterCollision: true,
|
|
175
|
+
}), ctx.rootDir);
|
|
176
|
+
let secretWritten = false;
|
|
177
|
+
let secretStatus = "not_applicable";
|
|
178
|
+
if (isConfidential(settings) && newApp.client_secret) {
|
|
179
|
+
const secretEnvKey = clientSecretEnvVar(clientKey);
|
|
180
|
+
const secretWriteResult = envManager.set(secretEnvKey, newApp.client_secret, {
|
|
181
|
+
writeOnce: true,
|
|
182
|
+
rejectOuterCollision: true,
|
|
183
|
+
});
|
|
184
|
+
switch (secretWriteResult.status) {
|
|
185
|
+
case "created":
|
|
186
|
+
case "updated":
|
|
187
|
+
secretWritten = true;
|
|
188
|
+
secretStatus = "written";
|
|
189
|
+
logger.success(` Client secret captured in ${secretEnvKey}.`);
|
|
190
|
+
break;
|
|
191
|
+
case "matched_existing_managed":
|
|
192
|
+
secretStatus = "matched_existing_managed";
|
|
193
|
+
logger.info(` Client secret already present in managed ${secretEnvKey}.`);
|
|
194
|
+
break;
|
|
195
|
+
case "skipped_existing_managed":
|
|
196
|
+
secretStatus = "skipped_existing_managed";
|
|
197
|
+
logger.warn(` Client secret already exists in managed ${secretEnvKey}. ` +
|
|
198
|
+
`Preserving the existing write-once value.`);
|
|
199
|
+
break;
|
|
200
|
+
case "rejected_outer_collision":
|
|
201
|
+
throw new Error(buildUnexpectedOuterCollisionError(secretEnvKey, ctx.rootDir, secretWriteResult.outerAssignments?.map((assignment) => assignment.rawLine) ?? []));
|
|
202
|
+
}
|
|
203
|
+
}
|
|
136
204
|
return {
|
|
137
205
|
clientKey,
|
|
138
206
|
clientId: newApp.client_id,
|
|
139
207
|
action: "created",
|
|
140
208
|
applicationType: settings.appType,
|
|
141
209
|
tokenEndpointAuthMethod: settings.tokenEndpointAuthMethod,
|
|
142
|
-
|
|
210
|
+
secretWritten,
|
|
211
|
+
secretStatus,
|
|
143
212
|
};
|
|
144
213
|
}
|
|
145
214
|
function resolveSettings(ecosystem, client, descriptor) {
|
|
@@ -280,7 +349,7 @@ async function patchApplication(auth0, existing, settings, ecosystem, clientConf
|
|
|
280
349
|
client_key: clientConfig.client_key,
|
|
281
350
|
descriptor: clientConfig.descriptor ?? "",
|
|
282
351
|
profile: clientConfig.profile,
|
|
283
|
-
managed_by:
|
|
352
|
+
managed_by: MANAGED_BY,
|
|
284
353
|
};
|
|
285
354
|
if (Object.keys(patch).length > 0) {
|
|
286
355
|
await auth0.updateApplication(existing.client_id, patch);
|
|
@@ -292,4 +361,27 @@ function mergeArrays(existing, desired) {
|
|
|
292
361
|
set.add(item);
|
|
293
362
|
return [...set];
|
|
294
363
|
}
|
|
364
|
+
function assertManagedClientEnvWrite(result, rootDir) {
|
|
365
|
+
if (result.status !== "rejected_outer_collision") {
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
throw new Error(buildUnexpectedOuterCollisionError(result.key, rootDir, result.outerAssignments?.map((assignment) => assignment.rawLine) ?? []));
|
|
369
|
+
}
|
|
370
|
+
function buildUnexpectedOuterCollisionError(envKey, rootDir, rawAssignments) {
|
|
371
|
+
const envPath = join(rootDir, ".env");
|
|
372
|
+
const entries = rawAssignments.length > 0
|
|
373
|
+
? rawAssignments.map((line) => ` ${maskSecretAssignment(line)}`)
|
|
374
|
+
: [` ${maskSecretAssignment(`${envKey}=<user-authored-value>`)}`];
|
|
375
|
+
return (`Invalid tool-managed client credential in ${envPath}\n\n` +
|
|
376
|
+
` ${envKey} exists outside the managed block, so the CLI refused to write a managed value.\n\n` +
|
|
377
|
+
` Remove the outer assignment and re-run reconciliation:\n` +
|
|
378
|
+
`${entries.join("\n")}`);
|
|
379
|
+
}
|
|
380
|
+
function maskSecretAssignment(line) {
|
|
381
|
+
const eqIdx = line.indexOf("=");
|
|
382
|
+
if (eqIdx === -1)
|
|
383
|
+
return line;
|
|
384
|
+
const key = line.slice(0, eqIdx).trim();
|
|
385
|
+
return key.includes("_SECRET") ? `${key}=********` : line;
|
|
386
|
+
}
|
|
295
387
|
//# sourceMappingURL=reconcile-client.js.map
|