@haven_ai/connect 0.1.11-alpha.0 → 0.1.13-alpha.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/dist/index.cjs CHANGED
@@ -8,6 +8,7 @@ var path = require('path');
8
8
  var child_process = require('child_process');
9
9
  var util = require('util');
10
10
  var mcp = require('@haven_ai/mcp');
11
+ var sdk = require('@haven_ai/sdk');
11
12
  var signer = require('@haven_ai/signer');
12
13
 
13
14
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -146,6 +147,7 @@ async function writeCredentialFiles(input) {
146
147
  safe_address: input.safeAddress,
147
148
  chain_id: input.chainId,
148
149
  network: input.network,
150
+ x402_binding_signer: input.x402BindingSigner,
149
151
  note: "Local signer credential. Haven backend never receives this private key."
150
152
  },
151
153
  input.warn
@@ -212,9 +214,9 @@ var MCP_RUNTIME_MANIFEST = {
212
214
  mcpPackage: "@haven_ai/mcp",
213
215
  mcpVersion: mcp.MCP_VERSION,
214
216
  sdkPackage: "@haven_ai/sdk",
215
- sdkVersion: "0.1.11-alpha.0",
217
+ sdkVersion: "0.1.13-alpha.0",
216
218
  signerPackage: "@haven_ai/signer",
217
- signerVersion: "0.1.11-alpha.0",
219
+ signerVersion: "0.1.13-alpha.0",
218
220
  minimumNodeVersion: "20.0.0",
219
221
  supportedClients: ["codex-cli", "codex-desktop", "claude-code"],
220
222
  requiredTools: mcp.registeredToolNames()
@@ -387,10 +389,16 @@ function buildHostedServer(hostedMcpUrl, apiKey, runtime) {
387
389
  headers: { Authorization: `Bearer ${apiKey}` }
388
390
  };
389
391
  }
390
- function buildSignerServer(signerPath, runtime) {
391
- const server = {
392
+ function resolveSignerLaunchSpec(input) {
393
+ return input.signerCommand ?? {
392
394
  command: "npx",
393
- args: ["-y", signerPackageName(), "--credentials", signerPath]
395
+ args: ["-y", signerPackageSpec(), "--credentials", input.signerPath]
396
+ };
397
+ }
398
+ function buildSignerServer(spec, runtime) {
399
+ const server = {
400
+ command: spec.command,
401
+ args: spec.args
394
402
  };
395
403
  if (runtime === "vscode" || runtime === "vscode-insiders") return { type: "stdio", ...server };
396
404
  return server;
@@ -423,7 +431,7 @@ function mergeCodexToml(existingToml, localMcpCommand) {
423
431
  `;
424
432
  return merged;
425
433
  }
426
- function mergeCodexTomlHosted(existingToml, hostedMcpUrl, apiKey, signerPath) {
434
+ function mergeCodexTomlHosted(existingToml, hostedMcpUrl, apiKey, signerSpec) {
427
435
  let next = removeTomlTableTree(removeTomlTableTree(existingToml, "mcp_servers.haven"), "mcp_servers.haven_signer");
428
436
  next = next.trimEnd();
429
437
  const block = [
@@ -432,8 +440,8 @@ function mergeCodexTomlHosted(existingToml, hostedMcpUrl, apiKey, signerPath) {
432
440
  `http_headers = { "Authorization" = ${tomlString(`Bearer ${apiKey}`)} }`,
433
441
  "",
434
442
  "[mcp_servers.haven_signer]",
435
- 'command = "npx"',
436
- `args = ["-y", ${tomlString(signerPackageSpec())}, "--credentials", ${tomlString(signerPath)}]`,
443
+ `command = ${tomlString(signerSpec.command)}`,
444
+ `args = [${signerSpec.args.map((arg) => tomlString(arg)).join(", ")}]`,
437
445
  "startup_timeout_sec = 120"
438
446
  ].join("\n");
439
447
  validateCodexToml(block, "Generated Codex Haven config");
@@ -450,7 +458,7 @@ async function writeJsonRuntimeConfig(input, target, serverRoot) {
450
458
  existing,
451
459
  serverRoot,
452
460
  buildHostedServer(input.hostedMcpUrl, input.apiKey, input.runtime),
453
- buildSignerServer(input.signerPath, input.runtime)
461
+ buildSignerServer(resolveSignerLaunchSpec(input), input.runtime)
454
462
  );
455
463
  await writeOwnerOnlyText(target, merged);
456
464
  return {
@@ -506,7 +514,7 @@ async function writeCodexConfig(input) {
506
514
  ]
507
515
  };
508
516
  }
509
- const merged = mergeCodexTomlHosted(existing ?? "", input.hostedMcpUrl, input.apiKey, input.signerPath);
517
+ const merged = mergeCodexTomlHosted(existing ?? "", input.hostedMcpUrl, input.apiKey, resolveSignerLaunchSpec(input));
510
518
  await writeOwnerOnlyText(target, merged);
511
519
  return {
512
520
  hostedConfigured: true,
@@ -791,9 +799,6 @@ function configTargetLabel(runtime) {
791
799
  return "runtime MCP config";
792
800
  }
793
801
  }
794
- function signerPackageName() {
795
- return signerPackageSpec();
796
- }
797
802
  async function acknowledgeLocalMcpConsent(identityPath, signerPath, log) {
798
803
  try {
799
804
  const input = await buildLocalMcpConsentInput(identityPath, signerPath);
@@ -907,7 +912,7 @@ async function probeLocalSignerCredential(signerPath) {
907
912
  }
908
913
  }
909
914
  async function probeLocalMcpTools(command, args, requiredTools, timeoutMs = 1e4) {
910
- return new Promise((resolve7) => {
915
+ return new Promise((resolve8) => {
911
916
  const child = child_process.spawn(command, args, { stdio: ["pipe", "pipe", "ignore"] });
912
917
  let stdout = "";
913
918
  let settled = false;
@@ -917,7 +922,7 @@ async function probeLocalMcpTools(command, args, requiredTools, timeoutMs = 1e4)
917
922
  settled = true;
918
923
  clearTimeout(timeout);
919
924
  child.kill();
920
- resolve7(result);
925
+ resolve8(result);
921
926
  };
922
927
  const timeout = setTimeout(() => finish({ status: "timeout" }), timeoutMs);
923
928
  child.on("error", () => finish({ status: "process_error" }));
@@ -1155,75 +1160,126 @@ async function assertFileExists(path, label) {
1155
1160
  throw new Error(`Missing ${label}: ${path}`);
1156
1161
  }
1157
1162
  }
1158
-
1159
- // src/skill-content.ts
1160
- var HAVEN_SKILL_MD = `---
1161
- name: haven-pay
1162
- description: Pay for things from the user's Haven wallet within their agent rules. Use when the user asks to send, pay, tip, or transfer crypto \u2014 or when a request hits an HTTP 402 (x402) paywall.
1163
- ---
1164
-
1165
- # Haven: pay from a Haven wallet
1166
-
1167
- This skill lets the agent make payments from the user's Haven wallet through
1168
- the Haven MCP tools. Every payment is checked against the agent's on-chain
1169
- budget before money moves; payments above the remaining budget wait for the
1170
- user's approval in Haven.
1171
-
1172
- ## When to use this skill
1173
-
1174
- - The user asks to send money, pay someone, tip, donate, or transfer tokens.
1175
- - A request returns HTTP 402 (x402): use the Haven pay tools to settle it,
1176
- then retry the original request.
1177
-
1178
- ## Identity and budget come from the tools \u2014 never assume them
1179
-
1180
- Do not guess the wallet address, network, or budget. Read them live:
1181
-
1182
- - \`haven_get_agent\` \u2014 agent identity, Haven wallet address, network.
1183
- - \`haven_get_allowances\` \u2014 current per-token budgets and what remains.
1184
-
1185
- Budgets reset on a period the user chose. If a payment exceeds the remaining
1186
- budget it is queued for the user to approve in the Haven dashboard \u2014 this is
1187
- normal, not an error.
1188
-
1189
- ## Paying
1190
-
1191
- - **Direct transfer:** \`haven_pay\` with recipient, amount, and token.
1192
- - **x402 paywall:** \`haven_quote_x402\` to get a quote, then
1193
- \`haven_pay_x402_quote\`. In the hosted setup the signing step happens in
1194
- the local Haven signer; follow the tool results \u2014 they tell you the next
1195
- action at every step. Retry the original request only when the result says
1196
- \`retry_original_x402_request\`.
1197
- - **Status:** \`haven_get_payment_status\` with a \`payment_id\` to check on
1198
- queued or in-flight payments. Do not poll in a tight loop.
1199
-
1200
- ## Approval semantics
1201
-
1202
- - A result with \`pending_approval\` means the payment exceeded the remaining
1203
- budget and is waiting for the user in Haven. Tell the user, then check
1204
- status later.
1205
- - Never ask the user for private keys and never try to sign anything
1206
- yourself \u2014 Haven signs. If a tool reports a missing or invalid credential,
1207
- tell the user to re-run the Haven setup command.
1208
-
1209
- ## Failure handling
1210
-
1211
- Haven errors are shaped \`{ error, status, details? }\` and written for
1212
- humans \u2014 surface the message verbatim. Common cases:
1213
-
1214
- - \`pending_approval\`: queued for the user's approval (see above).
1215
- - \`insufficient_funds\`: the Haven wallet doesn't hold enough of that token.
1216
- Suggest the user add funds in the Haven dashboard.
1217
- - Budget exceeded: tell the user how much remains (from
1218
- \`haven_get_allowances\`) and that they can raise the budget in Haven.
1219
-
1220
- ## Revoke
1221
-
1222
- If this agent's credential may have leaked, tell the user to pause or revoke
1223
- the agent in the Haven dashboard under Agents. New requests stop immediately
1224
- for that credential.
1225
- `;
1226
- var SKILL_FOLDER_NAME = "haven-pay";
1163
+ var execFileAsync2 = util.promisify(child_process.execFile);
1164
+ async function prepareSignerRuntime(input, deps = {}) {
1165
+ const homeDir = input.homeDir ?? os.homedir();
1166
+ const runtimeDirectory = path.resolve(homeDir, ".haven", "signer-runtime", MCP_RUNTIME_MANIFEST.signerVersion);
1167
+ const npmCacheDirectory = path.resolve(homeDir, ".haven", "npm-cache");
1168
+ const cliPath = path.join(runtimeDirectory, "node_modules", "@haven_ai", "signer", "dist", "cli.js");
1169
+ const messages = [];
1170
+ await promises.mkdir(runtimeDirectory, { recursive: true, mode: 448 });
1171
+ await promises.chmod(runtimeDirectory, 448).catch(() => void 0);
1172
+ await promises.mkdir(npmCacheDirectory, { recursive: true, mode: 448 });
1173
+ await promises.chmod(npmCacheDirectory, 448).catch(() => void 0);
1174
+ if (await installedRuntimeMatches2(runtimeDirectory, cliPath)) {
1175
+ messages.push(`Using existing local Haven signer runtime ${signerPackageSpec()}.`);
1176
+ } else {
1177
+ await installRuntimePackages2(runtimeDirectory, npmCacheDirectory, deps.runCommand);
1178
+ messages.push(`Installed local Haven signer runtime ${signerPackageSpec()}.`);
1179
+ }
1180
+ await assertFileExists2(cliPath, "local Haven signer CLI");
1181
+ const wrapperPath = path.join(input.credentialDirectory, "bin", "haven-signer.mjs");
1182
+ await writeWrapper2({ wrapperPath, cliPath, signerPath: input.signerPath });
1183
+ await writeRuntimeSidecar2({
1184
+ path: path.join(input.credentialDirectory, "signer-runtime.json"),
1185
+ wrapperPath,
1186
+ runtimeDirectory,
1187
+ npmCacheDirectory,
1188
+ cliPath
1189
+ });
1190
+ messages.push(`Prepared stable local Haven signer wrapper: ${wrapperPath}`);
1191
+ return {
1192
+ command: wrapperPath,
1193
+ args: [],
1194
+ wrapperPath,
1195
+ runtimeDirectory,
1196
+ npmCacheDirectory,
1197
+ cliPath,
1198
+ messages
1199
+ };
1200
+ }
1201
+ async function installRuntimePackages2(runtimeDirectory, npmCacheDirectory, runCommand) {
1202
+ const args = [
1203
+ "install",
1204
+ "--prefix",
1205
+ runtimeDirectory,
1206
+ "--cache",
1207
+ npmCacheDirectory,
1208
+ "--no-audit",
1209
+ "--no-fund",
1210
+ "--omit=dev",
1211
+ signerPackageSpec(),
1212
+ sdkPackageSpec()
1213
+ ];
1214
+ try {
1215
+ if (runCommand) await runCommand("npm", args);
1216
+ else await execFileAsync2("npm", args, { timeout: 12e4, maxBuffer: 1024 * 1024 });
1217
+ } catch (err) {
1218
+ throw new Error(
1219
+ `Could not install local Haven signer runtime ${signerPackageSpec()}: ${err instanceof Error ? err.message : String(err)}`
1220
+ );
1221
+ }
1222
+ }
1223
+ async function installedRuntimeMatches2(runtimeDirectory, cliPath) {
1224
+ try {
1225
+ await assertFileExists2(cliPath, "local Haven signer CLI");
1226
+ const [signerPackage, sdkPackage] = await Promise.all([
1227
+ readPackageJson2(path.join(runtimeDirectory, "node_modules", "@haven_ai", "signer", "package.json")),
1228
+ readPackageJson2(path.join(runtimeDirectory, "node_modules", "@haven_ai", "sdk", "package.json"))
1229
+ ]);
1230
+ return signerPackage.version === MCP_RUNTIME_MANIFEST.signerVersion && sdkPackage.version === MCP_RUNTIME_MANIFEST.sdkVersion;
1231
+ } catch {
1232
+ return false;
1233
+ }
1234
+ }
1235
+ async function readPackageJson2(path) {
1236
+ return JSON.parse(await promises.readFile(path, "utf8"));
1237
+ }
1238
+ async function writeWrapper2(input) {
1239
+ await promises.mkdir(path.dirname(input.wrapperPath), { recursive: true, mode: 448 });
1240
+ await promises.chmod(path.dirname(input.wrapperPath), 448).catch(() => void 0);
1241
+ const source = [
1242
+ "#!/usr/bin/env node",
1243
+ "import { spawn } from 'node:child_process'",
1244
+ "",
1245
+ `const cliPath = ${JSON.stringify(input.cliPath)}`,
1246
+ `const signerPath = ${JSON.stringify(input.signerPath)}`,
1247
+ "",
1248
+ "const child = spawn(process.execPath, [cliPath, '--credentials', signerPath, ...process.argv.slice(2)], {",
1249
+ " stdio: 'inherit',",
1250
+ "})",
1251
+ "",
1252
+ "child.on('exit', (code, signal) => {",
1253
+ " if (signal) process.kill(process.pid, signal)",
1254
+ " else process.exit(code ?? 1)",
1255
+ "})",
1256
+ ""
1257
+ ].join("\n");
1258
+ await promises.writeFile(input.wrapperPath, source, { mode: 448 });
1259
+ await promises.chmod(input.wrapperPath, 448).catch(() => void 0);
1260
+ }
1261
+ async function writeRuntimeSidecar2(input) {
1262
+ const value = {
1263
+ signer_package: MCP_RUNTIME_MANIFEST.signerPackage,
1264
+ signer_version: MCP_RUNTIME_MANIFEST.signerVersion,
1265
+ sdk_package: MCP_RUNTIME_MANIFEST.sdkPackage,
1266
+ sdk_version: MCP_RUNTIME_MANIFEST.sdkVersion,
1267
+ wrapper_path: input.wrapperPath,
1268
+ runtime_directory: input.runtimeDirectory,
1269
+ npm_cache_directory: input.npmCacheDirectory,
1270
+ cli_path: input.cliPath
1271
+ };
1272
+ await promises.writeFile(input.path, `${JSON.stringify(value, null, 2)}
1273
+ `, { mode: 384 });
1274
+ await promises.chmod(input.path, 384).catch(() => void 0);
1275
+ }
1276
+ async function assertFileExists2(path, label) {
1277
+ try {
1278
+ await promises.access(path);
1279
+ } catch {
1280
+ throw new Error(`Missing ${label}: ${path}`);
1281
+ }
1282
+ }
1227
1283
  async function acknowledgeLocalSignerConsent(signerPath, log) {
1228
1284
  try {
1229
1285
  const input = await buildSignerConsentInput(signerPath);
@@ -1295,7 +1351,7 @@ function writeLogChunk2(log, chunk) {
1295
1351
  }
1296
1352
 
1297
1353
  // src/runtime-install.ts
1298
- var execFileAsync2 = util.promisify(child_process.execFile);
1354
+ var execFileAsync3 = util.promisify(child_process.execFile);
1299
1355
  async function installRuntime(input, deps = {}) {
1300
1356
  const runtime = normalizeRuntime(input.runtime, deps.env);
1301
1357
  const profile = runtimeProfile(runtime, deps.env);
@@ -1356,7 +1412,20 @@ async function installRuntime(input, deps = {}) {
1356
1412
  ]
1357
1413
  };
1358
1414
  }
1359
- const configResult = runtime === "claude-code" ? localRuntime ? await configureClaudeCode(deps, localRuntimeInstall?.command ?? "") : await configureClaudeCodeHosted(deps, input) : await writeRuntimeConfig({
1415
+ let signerCommand;
1416
+ if (!localRuntime) {
1417
+ try {
1418
+ const signerRuntime = await prepareSignerForRuntime(input, deps);
1419
+ signerCommand = { command: signerRuntime.command, args: signerRuntime.args };
1420
+ consentMessages.push(...signerRuntime.messages);
1421
+ } catch (err) {
1422
+ consentMessages.push(
1423
+ `Could not pre-install the local Haven signer; falling back to npx launch: ${err instanceof Error ? err.message : String(err)}`
1424
+ );
1425
+ }
1426
+ }
1427
+ const signerRuntimePrepared = localRuntime ? void 0 : signerCommand !== void 0;
1428
+ const configResult = runtime === "claude-code" ? localRuntime ? await configureClaudeCode(deps, localRuntimeInstall?.command ?? "") : await configureClaudeCodeHosted(deps, input, signerCommand) : await writeRuntimeConfig({
1360
1429
  runtime,
1361
1430
  hostedMcpUrl: input.hostedMcpUrl,
1362
1431
  apiKey: input.apiKey,
@@ -1364,6 +1433,7 @@ async function installRuntime(input, deps = {}) {
1364
1433
  signerPath: input.signerPath,
1365
1434
  credentialDirectory: input.credentialDirectory,
1366
1435
  localMcpCommand: localRuntimeInstall?.command,
1436
+ signerCommand,
1367
1437
  homeDir: deps.homeDir,
1368
1438
  mode: localRuntime ? "local" : "hosted"
1369
1439
  });
@@ -1395,6 +1465,7 @@ async function installRuntime(input, deps = {}) {
1395
1465
  localMcpAcknowledged: localMcpConsent?.acknowledged,
1396
1466
  activationCommand: configResult.activationCommand,
1397
1467
  skillInstalled: skillInstall?.installed,
1468
+ signerRuntimePrepared,
1398
1469
  messages: [...consentMessages, ...localRuntimeInstall?.messages ?? [], ...configResult.messages, ...localProbeMessages, ...skillInstall?.messages ?? []]
1399
1470
  };
1400
1471
  }
@@ -1451,7 +1522,7 @@ async function configureClaudeCode(deps, localMcpCommand) {
1451
1522
  };
1452
1523
  }
1453
1524
  }
1454
- async function configureClaudeCodeHosted(deps, input) {
1525
+ async function configureClaudeCodeHosted(deps, input, signerCommand) {
1455
1526
  const runCommand = deps.runCommand ?? defaultRunCommand;
1456
1527
  const hostedJson = JSON.stringify({
1457
1528
  type: "http",
@@ -1460,8 +1531,8 @@ async function configureClaudeCodeHosted(deps, input) {
1460
1531
  });
1461
1532
  const signerJson = JSON.stringify({
1462
1533
  type: "stdio",
1463
- command: "npx",
1464
- args: ["-y", signerPackageSpec(), "--credentials", input.signerPath],
1534
+ command: signerCommand?.command ?? "npx",
1535
+ args: signerCommand?.args ?? ["-y", signerPackageSpec(), "--credentials", input.signerPath],
1465
1536
  env: {}
1466
1537
  });
1467
1538
  try {
@@ -1502,13 +1573,13 @@ async function configureClaudeCodeHosted(deps, input) {
1502
1573
  }
1503
1574
  }
1504
1575
  async function defaultRunCommand(command, args) {
1505
- await execFileAsync2(command, args, { timeout: 1e4 });
1576
+ await execFileAsync3(command, args, { timeout: 1e4 });
1506
1577
  }
1507
1578
  async function installClaudeSkill(homeDir) {
1508
1579
  try {
1509
- const skillDir = path.resolve(homeDir ?? os.homedir(), ".claude", "skills", SKILL_FOLDER_NAME);
1580
+ const skillDir = path.resolve(homeDir ?? os.homedir(), ".claude", "skills", sdk.SKILL_FOLDER_NAME);
1510
1581
  await promises.mkdir(skillDir, { recursive: true });
1511
- await promises.writeFile(path.join(skillDir, "SKILL.md"), HAVEN_SKILL_MD, "utf8");
1582
+ await promises.writeFile(path.join(skillDir, "SKILL.md"), sdk.HAVEN_SKILL_MD, "utf8");
1512
1583
  return {
1513
1584
  installed: true,
1514
1585
  messages: ["Installed the generic Haven payment skill (~/.claude/skills/haven-pay). It contains no secrets."]
@@ -1585,6 +1656,14 @@ async function prepareRuntimeForLocalMcp(input, deps) {
1585
1656
  homeDir: deps.homeDir
1586
1657
  });
1587
1658
  }
1659
+ async function prepareSignerForRuntime(input, deps) {
1660
+ const prepare = deps.prepareSignerRuntime ?? ((runtimeInput) => prepareSignerRuntime(runtimeInput, { runCommand: deps.runCommand }));
1661
+ return prepare({
1662
+ credentialDirectory: input.credentialDirectory,
1663
+ signerPath: input.signerPath,
1664
+ homeDir: deps.homeDir
1665
+ });
1666
+ }
1588
1667
  async function runLocalMcpProbe(runtimeInstall, deps) {
1589
1668
  const probe = deps.probeLocalMcpTools ?? probeLocalMcpTools;
1590
1669
  try {
@@ -1601,7 +1680,7 @@ function localRuntimePrepareErrorCode(err) {
1601
1680
  }
1602
1681
 
1603
1682
  // src/runtime.ts
1604
- var CONNECTOR_VERSION = "0.1.11-alpha.0";
1683
+ var CONNECTOR_VERSION = "0.1.13-alpha.0";
1605
1684
  async function runConnect(options, deps = {}) {
1606
1685
  const connectorVersion = options.connectorVersion ?? CONNECTOR_VERSION;
1607
1686
  const api = deps.api ?? createConnectApiClient(options.apiBaseUrl);
@@ -1666,10 +1745,18 @@ async function runConnect(options, deps = {}) {
1666
1745
  })),
1667
1746
  apiUrl: options.apiBaseUrl,
1668
1747
  hostedMcpUrl: registration.hosted_mcp_url,
1748
+ x402BindingSigner: setup.x402_binding_signer ?? void 0,
1669
1749
  warn: log
1670
1750
  });
1671
1751
  log(`Stored Haven identity credential locally: ${credentialPaths.identityPath}`);
1672
1752
  log(`Stored local signer credential locally: ${credentialPaths.signerPath}`);
1753
+ if (setup.x402_binding_signer) {
1754
+ log("Configured x402 binding signer for the local signer.");
1755
+ } else {
1756
+ log(
1757
+ "Warning: Haven did not provide an x402 binding signer, so x402 payments will not sign until HAVEN_X402_BINDING_SIGNER is set for the signer. Non-x402 payments are unaffected."
1758
+ );
1759
+ }
1673
1760
  const runtimeInstall = await runRuntimeInstall({
1674
1761
  runtime: options.runtime,
1675
1762
  hostedMcpUrl: registration.hosted_mcp_url,
@@ -1843,6 +1930,7 @@ exports.installRuntime = installRuntime;
1843
1930
  exports.mcpPackageSpec = mcpPackageSpec;
1844
1931
  exports.normalizeRuntime = normalizeRuntime;
1845
1932
  exports.parseArgs = parseArgs;
1933
+ exports.prepareSignerRuntime = prepareSignerRuntime;
1846
1934
  exports.redactSecrets = redactSecrets;
1847
1935
  exports.runConnect = runConnect;
1848
1936
  exports.runtimeInstallCapabilities = runtimeInstallCapabilities;