@integrity-labs/agt-cli 0.28.369 → 0.28.371

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.
@@ -8922,6 +8922,282 @@ function generateAgentsMd(input) {
8922
8922
  return out.join("\n");
8923
8923
  }
8924
8924
 
8925
+ // ../../packages/core/dist/provisioning/remote-mcp.js
8926
+ function envVarForToken(definitionId) {
8927
+ return `${definitionId.replace(/-/g, "_").toUpperCase()}_ACCESS_TOKEN`;
8928
+ }
8929
+ function credentialEnvVar(definitionId, credentialRef) {
8930
+ const idPart = definitionId.replace(/-/g, "_").toUpperCase();
8931
+ const credPart = credentialRef.replace(/-/g, "_").toUpperCase();
8932
+ return `${idPart}_${credPart}`;
8933
+ }
8934
+ function assertSafeRemoteMcpUrl(url, definitionId) {
8935
+ let u;
8936
+ try {
8937
+ u = new URL(url);
8938
+ } catch {
8939
+ throw new Error(`remoteMcp.url for '${definitionId}' is not a valid URL: ${url}`);
8940
+ }
8941
+ if (u.protocol !== "https:") {
8942
+ throw new Error(`remoteMcp.url for '${definitionId}' must be https (got ${u.protocol}//): ${url}`);
8943
+ }
8944
+ const host = u.hostname.toLowerCase();
8945
+ const blocked = host === "localhost" || host === "169.254.169.254" || // AWS/GCP/Azure instance metadata
8946
+ host === "metadata.google.internal" || /^127\./.test(host) || /^10\./.test(host) || /^192\.168\./.test(host) || /^169\.254\./.test(host) || // link-local
8947
+ /^172\.(1[6-9]|2\d|3[01])\./.test(host) || // 172.16.0.0/12
8948
+ host.endsWith(".internal") || host.endsWith(".local");
8949
+ if (blocked) {
8950
+ throw new Error(`remoteMcp.url for '${definitionId}' resolves to a disallowed (internal/link-local/metadata) host: ${host}`);
8951
+ }
8952
+ }
8953
+ function renderRemoteMcpSpec(definitionId, spec) {
8954
+ assertSafeRemoteMcpUrl(spec.url, definitionId);
8955
+ const headers = {};
8956
+ if (spec.auth) {
8957
+ const envVar = credentialEnvVar(definitionId, spec.auth.credential_ref);
8958
+ const value = `\${${envVar}}`;
8959
+ if (spec.auth.scheme === "bearer") {
8960
+ headers["Authorization"] = `Bearer ${value}`;
8961
+ } else {
8962
+ if (!spec.auth.header_name) {
8963
+ throw new Error(`remoteMcp.auth for '${definitionId}' uses scheme 'header' but no header_name`);
8964
+ }
8965
+ headers[spec.auth.header_name] = value;
8966
+ }
8967
+ }
8968
+ Object.assign(headers, spec.headers ?? {});
8969
+ return {
8970
+ type: spec.type ?? "http",
8971
+ url: spec.url,
8972
+ ...Object.keys(headers).length > 0 ? { headers } : {}
8973
+ };
8974
+ }
8975
+ function buildRemoteMcpEntry(definitionId, dbSpec) {
8976
+ const spec = dbSpec ?? INTEGRATION_REGISTRY.find((d) => d.id === definitionId)?.remoteMcp;
8977
+ if (spec) {
8978
+ return renderRemoteMcpSpec(definitionId, spec);
8979
+ }
8980
+ const provider = OAUTH_PROVIDERS[definitionId];
8981
+ if (!provider?.mcpUrl)
8982
+ return null;
8983
+ return {
8984
+ type: "http",
8985
+ url: provider.mcpUrl,
8986
+ headers: {
8987
+ Authorization: `Bearer \${${envVarForToken(definitionId)}}`
8988
+ }
8989
+ };
8990
+ }
8991
+ function buildHostBrokeredRemoteMcpEntry(url) {
8992
+ return { type: "http", url };
8993
+ }
8994
+ function extractHeaderVarName(value) {
8995
+ const m = /^\$\{([A-Za-z_][A-Za-z0-9_]*)\}$/.exec(value.trim());
8996
+ return m ? m[1] : null;
8997
+ }
8998
+ function buildLiveHeaderRemoteMcpProxyEntry(definitionId, dbSpec, paths) {
8999
+ const spec = dbSpec ?? INTEGRATION_REGISTRY.find((d) => d.id === definitionId)?.remoteMcp;
9000
+ if (!spec?.liveHeaderRefresh)
9001
+ return null;
9002
+ assertSafeRemoteMcpUrl(spec.url, definitionId);
9003
+ if (!spec.auth || spec.auth.scheme !== "header" || !spec.auth.header_name) {
9004
+ throw new Error(`remoteMcp.liveHeaderRefresh for '${definitionId}' requires a header-scheme 'auth' with a header_name`);
9005
+ }
9006
+ const extraPairs = [];
9007
+ for (const [header, value] of Object.entries(spec.headers ?? {})) {
9008
+ const varName = extractHeaderVarName(value);
9009
+ if (varName)
9010
+ extraPairs.push(`${header}:${varName}`);
9011
+ }
9012
+ return {
9013
+ command: "node",
9014
+ args: [paths.proxyPath],
9015
+ env: {
9016
+ AGT_REMOTE_MCP_URL: spec.url,
9017
+ AGT_REMOTE_MCP_TOKEN_FILE: paths.tokenFile,
9018
+ AGT_REMOTE_MCP_TOKEN_VAR: credentialEnvVar(definitionId, spec.auth.credential_ref),
9019
+ AGT_REMOTE_MCP_AUTH_HEADER: spec.auth.header_name,
9020
+ AGT_REMOTE_MCP_LABEL: definitionId,
9021
+ ...extraPairs.length > 0 ? { AGT_REMOTE_MCP_EXTRA_HEADERS: extraPairs.join(",") } : {}
9022
+ }
9023
+ };
9024
+ }
9025
+ function buildOAuthRemoteMcpProxyEntry(definitionId, paths) {
9026
+ const def = INTEGRATION_REGISTRY.find((d) => d.id === definitionId);
9027
+ if (def?.remoteMcp)
9028
+ return null;
9029
+ const provider = OAUTH_PROVIDERS[definitionId];
9030
+ if (!provider?.mcpUrl)
9031
+ return null;
9032
+ return {
9033
+ command: "node",
9034
+ args: [paths.proxyPath],
9035
+ env: {
9036
+ AGT_REMOTE_MCP_URL: provider.mcpUrl,
9037
+ AGT_REMOTE_MCP_TOKEN_FILE: paths.tokenFile,
9038
+ AGT_REMOTE_MCP_TOKEN_VAR: envVarForToken(definitionId),
9039
+ AGT_REMOTE_MCP_LABEL: definitionId,
9040
+ // ENG-6948: cap the agent's exposed surface to the curated allowlist. The
9041
+ // proxy filters tools/list and gates tools/call against this set. Omitted
9042
+ // when the provider has no allowlist, leaving the proxy a pass-through.
9043
+ ...provider.toolAllowlist && provider.toolAllowlist.length > 0 ? { AGT_REMOTE_MCP_TOOL_ALLOWLIST: provider.toolAllowlist.join(",") } : {},
9044
+ // CS-1446: toolsets to pre-activate at session start so gated tools are
9045
+ // advertised in the connect-time tools/list the harness freezes. Omitted
9046
+ // when the provider configures none (no pre-enable, the default).
9047
+ ...provider.preEnableToolsets && provider.preEnableToolsets.length > 0 ? { AGT_REMOTE_MCP_PREENABLE_TOOLSETS: provider.preEnableToolsets.join(",") } : {}
9048
+ }
9049
+ };
9050
+ }
9051
+
9052
+ // ../../packages/core/dist/provisioning/native-mcp.js
9053
+ function buildNativeMcpEntry(spec, ctx) {
9054
+ const resolvedCommand = resolveTemplate(spec.command, ctx);
9055
+ if (resolvedCommand.omit) {
9056
+ throw new Error("NativeMcpSpec: empty_if_no_env is only valid in env values (not in `command`)");
9057
+ }
9058
+ const command = resolvedCommand.value;
9059
+ const args = spec.args.map((a, i) => {
9060
+ const resolved = resolveTemplate(a, ctx);
9061
+ if (resolved.omit) {
9062
+ throw new Error(`NativeMcpSpec: empty_if_no_env is only valid in env values (not in args[${i}])`);
9063
+ }
9064
+ return resolved.value;
9065
+ });
9066
+ if (spec.env === void 0) {
9067
+ return { command, args };
9068
+ }
9069
+ const env2 = {};
9070
+ for (const [k, raw] of Object.entries(spec.env)) {
9071
+ const { value, omit } = resolveTemplate(raw, ctx);
9072
+ if (omit)
9073
+ continue;
9074
+ env2[k] = value;
9075
+ }
9076
+ return { command, args, env: env2 };
9077
+ }
9078
+ function resolveTemplate(input, ctx) {
9079
+ const TOKEN = /\{\{([^}]+)\}\}/g;
9080
+ const hasEmptyIfNoEnv = /\{\{\s*empty_if_no_env\./.test(input);
9081
+ const isWholeValueEmptyIfNoEnv = /^\{\{\s*empty_if_no_env\.[^}]+\}\}$/.test(input);
9082
+ if (hasEmptyIfNoEnv && !isWholeValueEmptyIfNoEnv) {
9083
+ throw new Error(`NativeMcpSpec: empty_if_no_env must be the sole content of the value, never mixed with literal text or other tokens (value: ${JSON.stringify(input)})`);
9084
+ }
9085
+ let omit = false;
9086
+ const value = input.replace(TOKEN, (whole, expr) => {
9087
+ const trimmed = expr.trim();
9088
+ if (trimmed === "agent_id")
9089
+ return ctx.agentId;
9090
+ if (trimmed === "agent_code_name")
9091
+ return ctx.agentCodeName;
9092
+ if (trimmed === "integration_id")
9093
+ return ctx.integration?.id ?? "";
9094
+ if (trimmed.startsWith("process_env.")) {
9095
+ const name = trimmed.slice("process_env.".length);
9096
+ return process.env[name] ?? "";
9097
+ }
9098
+ if (trimmed.startsWith("empty_if_no_env.")) {
9099
+ const name = trimmed.slice("empty_if_no_env.".length);
9100
+ const v = process.env[name] ?? "";
9101
+ if (v.length === 0) {
9102
+ omit = true;
9103
+ return "";
9104
+ }
9105
+ return v;
9106
+ }
9107
+ return whole;
9108
+ });
9109
+ return { value, omit };
9110
+ }
9111
+
9112
+ // ../../packages/core/dist/provisioning/frameworks/opencode/integrations.js
9113
+ function toOpencodeEnvRefs(value) {
9114
+ return value.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_m, name) => `{env:${name}}`);
9115
+ }
9116
+ function envPrefix(definitionId) {
9117
+ return definitionId.toUpperCase().replace(/[^A-Z0-9]/g, "_");
9118
+ }
9119
+ function remoteMcpCredentialEnvVar(definitionId, credentialRef) {
9120
+ return `${envPrefix(definitionId)}_${credentialRef.replace(/-/g, "_").toUpperCase()}`;
9121
+ }
9122
+ function toOpencodeRemoteEntry(entry) {
9123
+ const headers = entry.headers ? Object.fromEntries(Object.entries(entry.headers).map(([k, v]) => [k, toOpencodeEnvRefs(v)])) : void 0;
9124
+ return {
9125
+ type: "remote",
9126
+ url: entry.url,
9127
+ ...headers && Object.keys(headers).length > 0 ? { headers } : {},
9128
+ enabled: true
9129
+ };
9130
+ }
9131
+ function toOpencodeLocalEntry(entry) {
9132
+ const environment = entry.env ? Object.fromEntries(Object.entries(entry.env).map(([k, v]) => [k, toOpencodeEnvRefs(v)])) : void 0;
9133
+ return {
9134
+ type: "local",
9135
+ command: [entry.command, ...entry.args],
9136
+ ...environment && Object.keys(environment).length > 0 ? { environment } : {},
9137
+ enabled: true
9138
+ };
9139
+ }
9140
+ function buildOpencodeIntegrationServers(integrations, ctx) {
9141
+ const servers = {};
9142
+ const envUpdates = {};
9143
+ for (const integration of integrations) {
9144
+ const prefix = envPrefix(integration.definition_id);
9145
+ const creds = integration.credentials ?? {};
9146
+ if (integration.auth_type === "oauth2" || integration.auth_type === "github_app") {
9147
+ const token = creds.access_token;
9148
+ if (typeof token === "string" && token)
9149
+ envUpdates[`${prefix}_ACCESS_TOKEN`] = token;
9150
+ } else if (integration.auth_type === "api_key") {
9151
+ const token = creds.api_key;
9152
+ if (typeof token === "string" && token)
9153
+ envUpdates[`${prefix}_API_KEY`] = token;
9154
+ }
9155
+ const remoteSpec = integration.remoteMcp ?? INTEGRATION_REGISTRY.find((d) => d.id === integration.definition_id)?.remoteMcp;
9156
+ if (remoteSpec?.auth) {
9157
+ const ref = remoteSpec.auth.credential_ref;
9158
+ const value = creds[ref];
9159
+ if (typeof value === "string" && value) {
9160
+ envUpdates[remoteMcpCredentialEnvVar(integration.definition_id, ref)] = value;
9161
+ }
9162
+ }
9163
+ if (integration.config) {
9164
+ for (const [key, value] of Object.entries(integration.config)) {
9165
+ if (typeof value === "string" && value) {
9166
+ const upper = key.toUpperCase();
9167
+ const envKey = upper.startsWith(`${prefix}_`) ? upper : `${prefix}_${upper}`;
9168
+ envUpdates[envKey] = value;
9169
+ }
9170
+ }
9171
+ }
9172
+ }
9173
+ for (const integration of integrations) {
9174
+ const defaults = integration.remoteMcp?.envDefaults ?? INTEGRATION_REGISTRY.find((d) => d.id === integration.definition_id)?.remoteMcp?.envDefaults;
9175
+ if (!defaults)
9176
+ continue;
9177
+ for (const [key, value] of Object.entries(defaults)) {
9178
+ if (!(key in envUpdates))
9179
+ envUpdates[key] = value;
9180
+ }
9181
+ }
9182
+ for (const integration of integrations) {
9183
+ const def = INTEGRATION_REGISTRY.find((d) => d.id === integration.definition_id);
9184
+ const remote = buildRemoteMcpEntry(integration.definition_id, integration.remoteMcp ?? null);
9185
+ if (remote) {
9186
+ servers[integration.definition_id] = toOpencodeRemoteEntry(remote);
9187
+ continue;
9188
+ }
9189
+ if (def?.nativeMcp) {
9190
+ const key = def.nativeMcp.key ?? integration.definition_id;
9191
+ servers[key] = toOpencodeLocalEntry(buildNativeMcpEntry(def.nativeMcp, {
9192
+ agentId: ctx.agentId,
9193
+ agentCodeName: ctx.agentCodeName,
9194
+ integration
9195
+ }));
9196
+ }
9197
+ }
9198
+ return { servers, envUpdates };
9199
+ }
9200
+
8925
9201
  // ../../packages/core/dist/provisioning/frameworks/opencode/opencode-client.js
8926
9202
  var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
8927
9203
  var HttpOpencodeClient = class _HttpOpencodeClient {
@@ -9306,6 +9582,22 @@ function readEnvIntegrations(codeName) {
9306
9582
  }
9307
9583
  return out;
9308
9584
  }
9585
+ var INTEGRATION_KEYS_FILE = "integration-mcp-keys.json";
9586
+ function readIntegrationServerKeys(codeName) {
9587
+ const p2 = join(agentDir(codeName), INTEGRATION_KEYS_FILE);
9588
+ if (!existsSync2(p2))
9589
+ return [];
9590
+ try {
9591
+ const parsed = JSON.parse(readFileSync3(p2, "utf8"));
9592
+ return Array.isArray(parsed) ? parsed.filter((k) => typeof k === "string") : [];
9593
+ } catch {
9594
+ return [];
9595
+ }
9596
+ }
9597
+ function writeIntegrationServerKeys(codeName, keys) {
9598
+ mkdirSync(agentDir(codeName), { recursive: true });
9599
+ writeFileSync2(join(agentDir(codeName), INTEGRATION_KEYS_FILE), JSON.stringify([...keys].sort(), null, 2));
9600
+ }
9309
9601
  var ENV_SUBSTITUTION = /^\{env:([A-Za-z_][A-Za-z0-9_]*)\}$/;
9310
9602
  function readChannelServerEnv(codeName, channelId) {
9311
9603
  const mcp = readConfig(codeName)["mcp"];
@@ -9407,6 +9699,50 @@ var opencodeAdapter = {
9407
9699
  cfg["mcp"] = mcp;
9408
9700
  writeConfig(codeName, cfg);
9409
9701
  },
9702
+ /**
9703
+ * ENG-7976: render org/team/agent-scoped integrations into opencode's `mcp`
9704
+ * block. Follow-up to ENG-7959 - the adapter previously implemented no
9705
+ * `writeIntegrations`, so the manager's `/host/agent-integrations` provisioning
9706
+ * path was silently skipped for opencode and every integration MCP server
9707
+ * (remote_mcp catalog + OAuth remotes + data-driven native) was dropped.
9708
+ *
9709
+ * The mapping is pure (`buildOpencodeIntegrationServers`, reusing the same
9710
+ * shared builders as the claude-code adapter). Secrets never enter the
9711
+ * drift-checked config: they are seeded into `.env.integrations` and referenced
9712
+ * as `{env:VAR}`, resolved by the runtime materializer at serve spawn - the same
9713
+ * contract `writeChannelCredentials` uses. Integration servers are merged INTO
9714
+ * the existing `mcp` map (never a wholesale replace) so the generator's
9715
+ * `augmented` entry and any channel servers already on disk survive.
9716
+ *
9717
+ * Stale integration servers (an integration disconnected since the last write)
9718
+ * are pruned: any entry previously written by this method that is not in the
9719
+ * fresh set is removed, while the `augmented` entry, channel servers and managed
9720
+ * toolkit servers are left untouched.
9721
+ */
9722
+ writeIntegrations(codeName, integrations, agentId) {
9723
+ const { servers, envUpdates } = buildOpencodeIntegrationServers(integrations, {
9724
+ agentId: agentId ?? "",
9725
+ agentCodeName: codeName
9726
+ });
9727
+ upsertEnvIntegrations(codeName, envUpdates);
9728
+ const previous = readIntegrationServerKeys(codeName);
9729
+ if (Object.keys(servers).length === 0 && previous.length === 0 && !existsSync2(configPath(codeName))) {
9730
+ return;
9731
+ }
9732
+ const cfg = readConfig(codeName);
9733
+ const mcp = cfg["mcp"] ?? {};
9734
+ const fresh = new Set(Object.keys(servers));
9735
+ for (const key of previous) {
9736
+ if (!fresh.has(key))
9737
+ delete mcp[key];
9738
+ }
9739
+ for (const [serverId, entry] of Object.entries(servers)) {
9740
+ mcp[serverId] = entry;
9741
+ }
9742
+ cfg["mcp"] = mcp;
9743
+ writeConfig(codeName, cfg);
9744
+ writeIntegrationServerKeys(codeName, Object.keys(servers));
9745
+ },
9410
9746
  removeMcpServer(codeName, serverId) {
9411
9747
  const cfg = readConfig(codeName);
9412
9748
  const mcp = cfg["mcp"];
@@ -9853,6 +10189,12 @@ async function spawnServe(config, session) {
9853
10189
  mkdirSync3(join3(homedir3(), ".augmented", codeName), { recursive: true });
9854
10190
  const serveEnv = {
9855
10191
  ...process.env,
10192
+ // ENG-7976: integration + channel credential secrets so the adapter's
10193
+ // `{env:VAR}` refs (remote MCP auth headers, native MCP env, channel tokens)
10194
+ // resolve at materialization. Layered after process.env (so the file supplies
10195
+ // creds absent from the host env) and before the per-agent provider key +
10196
+ // manager runtime env (which win for their own keys).
10197
+ ...readAgentIntegrationsEnv(codeName),
9856
10198
  ...readAgentProviderEnv(codeName),
9857
10199
  ...stripUndefined(config.serveEnv ?? {}),
9858
10200
  OPENCODE_SERVER_PASSWORD: password,
@@ -9991,6 +10333,42 @@ function readAgentProviderEnv(codeName, dir) {
9991
10333
  }
9992
10334
  return out;
9993
10335
  }
10336
+ var INTEGRATIONS_ENV_BLOCKLIST = /* @__PURE__ */ new Set([
10337
+ "PATH",
10338
+ "HOME",
10339
+ "USER",
10340
+ "SHELL",
10341
+ "IFS",
10342
+ "ENV",
10343
+ "BASH_ENV",
10344
+ "NODE_OPTIONS",
10345
+ "LD_PRELOAD",
10346
+ "LD_LIBRARY_PATH",
10347
+ "DYLD_INSERT_LIBRARIES",
10348
+ "DYLD_LIBRARY_PATH"
10349
+ ]);
10350
+ function readAgentIntegrationsEnv(codeName, dir) {
10351
+ const file = join3(dir ?? join3(homedir3(), ".augmented", codeName), ".env.integrations");
10352
+ const out = {};
10353
+ try {
10354
+ if (!existsSync4(file)) return out;
10355
+ for (const raw of readFileSync5(file, "utf-8").split("\n")) {
10356
+ const line2 = raw.trim();
10357
+ if (!line2 || line2.startsWith("#")) continue;
10358
+ const eq = line2.indexOf("=");
10359
+ if (eq <= 0) continue;
10360
+ const key = line2.slice(0, eq).trim();
10361
+ if (INTEGRATIONS_ENV_BLOCKLIST.has(key)) continue;
10362
+ let val = line2.slice(eq + 1).trim();
10363
+ if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
10364
+ val = val.slice(1, -1);
10365
+ }
10366
+ out[key] = val;
10367
+ }
10368
+ } catch {
10369
+ }
10370
+ return out;
10371
+ }
9994
10372
 
9995
10373
  // src/lib/persistent-session.ts
9996
10374
  import { randomUUID as randomUUID2 } from "crypto";
@@ -11620,6 +11998,11 @@ export {
11620
11998
  INTEGRATION_REGISTRY,
11621
11999
  getIntegration,
11622
12000
  OAUTH_PROVIDERS,
12001
+ buildRemoteMcpEntry,
12002
+ buildHostBrokeredRemoteMcpEntry,
12003
+ buildLiveHeaderRemoteMcpProxyEntry,
12004
+ buildOAuthRemoteMcpProxyEntry,
12005
+ buildNativeMcpEntry,
11623
12006
  toOpencodeModel,
11624
12007
  mergeOpencodeConfigArtifact,
11625
12008
  readChannelServerEnv,
@@ -11753,4 +12136,4 @@ export {
11753
12136
  stopAllSessionsAndWait,
11754
12137
  getProjectDir
11755
12138
  };
11756
- //# sourceMappingURL=chunk-RIWZIYJ7.js.map
12139
+ //# sourceMappingURL=chunk-NDF3YMP5.js.map