@sentry/junior 0.42.0 → 0.43.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.
@@ -1347,7 +1347,7 @@ var stringMapSchema = z.record(z.string(), z.unknown()).transform((record, ctx)
1347
1347
  }
1348
1348
  return result;
1349
1349
  });
1350
- var apiDomainsSchema = z.array(z.unknown()).min(1, {
1350
+ var domainsSchema = z.array(z.unknown()).min(1, {
1351
1351
  error: "must be a non-empty array of strings"
1352
1352
  }).transform((domains, ctx) => {
1353
1353
  return domains.map((rawDomain) => {
@@ -1370,7 +1370,7 @@ var apiDomainsSchema = z.array(z.unknown()).min(1, {
1370
1370
  });
1371
1371
  });
1372
1372
  var baseCredentialsSchema = z.object({
1373
- "api-domains": apiDomainsSchema,
1373
+ domains: domainsSchema.optional(),
1374
1374
  "api-headers": stringMapSchema.optional(),
1375
1375
  "auth-token-env": envVarString,
1376
1376
  "auth-token-placeholder": nonEmptyTrimmedString.optional()
@@ -1438,7 +1438,7 @@ var manifestSourceSchema = z.object({
1438
1438
  "config-keys": z.array(z.string(), {
1439
1439
  error: "must be an array when provided"
1440
1440
  }).optional(),
1441
- "api-domains": apiDomainsSchema.optional(),
1441
+ domains: domainsSchema.optional(),
1442
1442
  "api-headers": stringMapSchema.optional(),
1443
1443
  "command-env": stringMapSchema.optional(),
1444
1444
  credentials: z.record(z.string(), z.unknown(), {
@@ -1493,9 +1493,13 @@ function normalizeStringMap(value, prefix, options = {}) {
1493
1493
  }
1494
1494
  return value;
1495
1495
  }
1496
+ function envReferences(value) {
1497
+ return Array.from(value.matchAll(ENV_PLACEHOLDER_RE), (match) => {
1498
+ return match[1];
1499
+ });
1500
+ }
1496
1501
  function assertDeclaredEnvReferences(value, envVars, context) {
1497
- for (const match of value.matchAll(ENV_PLACEHOLDER_RE)) {
1498
- const name = match[1];
1502
+ for (const name of envReferences(value)) {
1499
1503
  if (!Object.prototype.hasOwnProperty.call(envVars, name)) {
1500
1504
  throw new Error(
1501
1505
  `${context} references env var ${name} which is not declared in env-vars`
@@ -1518,21 +1522,25 @@ function normalizeRequiredApiHeaders(value, prefix, envVars) {
1518
1522
  }
1519
1523
  return apiHeaders;
1520
1524
  }
1521
- function assertCommandEnvReferencesArePublic(value, envVars, context) {
1522
- for (const match of value.matchAll(ENV_PLACEHOLDER_RE)) {
1523
- const name = match[1];
1525
+ function assertCommandEnvReferencesDeclared(value, envVars, context) {
1526
+ for (const name of envReferences(value)) {
1524
1527
  if (!Object.prototype.hasOwnProperty.call(envVars, name)) {
1525
1528
  throw new Error(
1526
1529
  `${context} references env var ${name} which is not declared in env-vars`
1527
1530
  );
1528
1531
  }
1529
- if (envVars[name]?.default === void 0) {
1530
- throw new Error(
1531
- `${context} references env var ${name}, but command-env env vars must declare defaults`
1532
- );
1533
- }
1534
1532
  }
1535
1533
  }
1534
+ function expandCommandEnvPlaceholders(template, envVars, context) {
1535
+ return template.replace(ENV_PLACEHOLDER_RE, (match, name) => {
1536
+ const varName = name;
1537
+ const declaration = envVars[varName];
1538
+ if (declaration?.default === void 0) {
1539
+ return match;
1540
+ }
1541
+ return expandEnvPlaceholders(match, envVars, context);
1542
+ });
1543
+ }
1536
1544
  function normalizeCommandEnv(value, prefix, envVars) {
1537
1545
  const env = normalizeStringMap(value, prefix);
1538
1546
  if (!env) {
@@ -1542,15 +1550,47 @@ function normalizeCommandEnv(value, prefix, envVars) {
1542
1550
  if (!ENV_VAR_NAME_RE.test(key)) {
1543
1551
  throw new Error(`${prefix}.${key} must be an uppercase env var name`);
1544
1552
  }
1545
- assertCommandEnvReferencesArePublic(envValue, envVars, `${prefix}.${key}`);
1553
+ assertCommandEnvReferencesDeclared(envValue, envVars, `${prefix}.${key}`);
1546
1554
  }
1547
1555
  return Object.fromEntries(
1548
1556
  Object.entries(env).map(([key, envValue]) => [
1549
1557
  key,
1550
- expandEnvPlaceholders(envValue, envVars, `${prefix}.${key}`)
1558
+ expandCommandEnvPlaceholders(envValue, envVars, `${prefix}.${key}`)
1551
1559
  ])
1552
1560
  );
1553
1561
  }
1562
+ function assertCommandEnvDoesNotExposeHostSecretRefs(commandEnv, apiHeaders, credentials, oauth, pluginName) {
1563
+ if (!commandEnv) {
1564
+ return;
1565
+ }
1566
+ const hostOnlyRefs = /* @__PURE__ */ new Set();
1567
+ for (const value of Object.values(apiHeaders ?? {})) {
1568
+ for (const name of envReferences(value)) {
1569
+ hostOnlyRefs.add(name);
1570
+ }
1571
+ }
1572
+ if (credentials) {
1573
+ hostOnlyRefs.add(credentials.authTokenEnv);
1574
+ if (credentials.type === "github-app") {
1575
+ hostOnlyRefs.add(credentials.appIdEnv);
1576
+ hostOnlyRefs.add(credentials.privateKeyEnv);
1577
+ hostOnlyRefs.add(credentials.installationIdEnv);
1578
+ }
1579
+ }
1580
+ if (oauth) {
1581
+ hostOnlyRefs.add(oauth.clientIdEnv);
1582
+ hostOnlyRefs.add(oauth.clientSecretEnv);
1583
+ }
1584
+ for (const [key, value] of Object.entries(commandEnv)) {
1585
+ for (const name of envReferences(value)) {
1586
+ if (hostOnlyRefs.has(name)) {
1587
+ throw new Error(
1588
+ `Plugin ${pluginName} command-env.${key} references env var ${name}, but credential/API header env vars must stay host-only`
1589
+ );
1590
+ }
1591
+ }
1592
+ }
1593
+ }
1554
1594
  function normalizeCredentials(data, name) {
1555
1595
  const schema = data.type === "oauth-bearer" ? oauthBearerCredentialsSchema : data.type === "github-app" ? githubAppCredentialsSchema : void 0;
1556
1596
  if (!schema) {
@@ -1562,6 +1602,10 @@ function normalizeCredentials(data, name) {
1562
1602
  if (!result.success) {
1563
1603
  throw new Error(issueMessage(result.error, `Plugin ${name} credentials`));
1564
1604
  }
1605
+ if (!result.data.domains) {
1606
+ throw new Error(`Plugin ${name} credentials requires domains`);
1607
+ }
1608
+ const domains = result.data.domains;
1565
1609
  if (result.data.type === "oauth-bearer") {
1566
1610
  const apiHeaders2 = result.data["api-headers"] ? normalizeStringMap(
1567
1611
  result.data["api-headers"],
@@ -1570,7 +1614,7 @@ function normalizeCredentials(data, name) {
1570
1614
  ) : void 0;
1571
1615
  return {
1572
1616
  type: "oauth-bearer",
1573
- apiDomains: result.data["api-domains"],
1617
+ domains,
1574
1618
  ...apiHeaders2 ? { apiHeaders: apiHeaders2 } : {},
1575
1619
  authTokenEnv: result.data["auth-token-env"],
1576
1620
  ...result.data["auth-token-placeholder"] ? { authTokenPlaceholder: result.data["auth-token-placeholder"] } : {}
@@ -1583,7 +1627,7 @@ function normalizeCredentials(data, name) {
1583
1627
  ) : void 0;
1584
1628
  return {
1585
1629
  type: "github-app",
1586
- apiDomains: result.data["api-domains"],
1630
+ domains,
1587
1631
  ...apiHeaders ? { apiHeaders } : {},
1588
1632
  authTokenEnv: result.data["auth-token-env"],
1589
1633
  ...result.data["auth-token-placeholder"] ? { authTokenPlaceholder: result.data["auth-token-placeholder"] } : {},
@@ -1722,7 +1766,7 @@ var envVarDeclarationSchema = z.preprocess(
1722
1766
  (value) => value === null || value === void 0 ? {} : value,
1723
1767
  z.object({
1724
1768
  default: z.string().optional()
1725
- }).passthrough()
1769
+ }).strict()
1726
1770
  );
1727
1771
  function normalizeEnvVars(data, pluginName) {
1728
1772
  const normalized = {};
@@ -1825,9 +1869,9 @@ function parsePluginManifest(raw, dir) {
1825
1869
  `Plugin ${parsedYaml.name ?? "unknown"} config-keys must be an array when provided`
1826
1870
  );
1827
1871
  }
1828
- if (path3 === "api-domains") {
1872
+ if (path3 === "domains") {
1829
1873
  throw new Error(
1830
- `Plugin ${parsedYaml.name ?? "unknown"} api-domains must be a non-empty array of domains`
1874
+ `Plugin ${parsedYaml.name ?? "unknown"} ${path3} must be a non-empty array of domains`
1831
1875
  );
1832
1876
  }
1833
1877
  if (path3 === "api-headers") {
@@ -1898,11 +1942,14 @@ function parsePluginManifest(raw, dir) {
1898
1942
  `Plugin ${data.name} api-headers`,
1899
1943
  envVars
1900
1944
  ) : void 0;
1901
- if (apiHeaders && !data["api-domains"]) {
1902
- throw new Error(`Plugin ${data.name} api-headers requires api-domains`);
1945
+ const domains = data.domains;
1946
+ if (apiHeaders && !domains) {
1947
+ throw new Error(`Plugin ${data.name} api-headers requires domains`);
1903
1948
  }
1904
- if (data["api-domains"] && !apiHeaders) {
1905
- throw new Error(`Plugin ${data.name} api-domains requires api-headers`);
1949
+ if (domains && !apiHeaders && !data.credentials) {
1950
+ throw new Error(
1951
+ `Plugin ${data.name} domains requires credentials or api-headers`
1952
+ );
1906
1953
  }
1907
1954
  const commandEnv = data["command-env"] ? normalizeCommandEnv(
1908
1955
  data["command-env"],
@@ -1923,7 +1970,7 @@ function parsePluginManifest(raw, dir) {
1923
1970
  description: data.description,
1924
1971
  capabilities,
1925
1972
  configKeys,
1926
- ...data["api-domains"] ? { apiDomains: data["api-domains"] } : {},
1973
+ ...domains ? { domains } : {},
1927
1974
  ...apiHeaders ? { apiHeaders } : {},
1928
1975
  ...commandEnv ? { commandEnv } : {},
1929
1976
  ...Object.keys(envVars).length > 0 ? { envVars } : {},
@@ -1970,6 +2017,13 @@ function parsePluginManifest(raw, dir) {
1970
2017
  ...tokenExtraHeaders ? { tokenExtraHeaders } : {}
1971
2018
  };
1972
2019
  }
2020
+ assertCommandEnvDoesNotExposeHostSecretRefs(
2021
+ data["command-env"],
2022
+ apiHeaders,
2023
+ credentials,
2024
+ manifest.oauth,
2025
+ data.name
2026
+ );
1973
2027
  if (data.target) {
1974
2028
  const result = targetSourceSchema.safeParse(data.target);
1975
2029
  if (!result.success) {
@@ -2023,14 +2077,45 @@ function mergeHeaderTransforms(transforms) {
2023
2077
  }));
2024
2078
  }
2025
2079
 
2080
+ // src/chat/plugins/command-env.ts
2081
+ var ENV_PLACEHOLDER_RE2 = /\$\{([A-Z_][A-Z0-9_]*)\}/g;
2082
+ function resolveValue(manifest, value) {
2083
+ let missing = false;
2084
+ const resolved = value.replace(ENV_PLACEHOLDER_RE2, (match, name) => {
2085
+ const envName = name;
2086
+ const declaration = manifest.envVars?.[envName];
2087
+ if (!declaration || declaration.default !== void 0) {
2088
+ return match;
2089
+ }
2090
+ const hostValue = process.env[envName];
2091
+ if (hostValue === void 0 || hostValue === "") {
2092
+ missing = true;
2093
+ return "";
2094
+ }
2095
+ return hostValue;
2096
+ });
2097
+ return missing ? void 0 : resolved;
2098
+ }
2099
+ function resolvePluginCommandEnv(manifest) {
2100
+ const env = {};
2101
+ for (const [key, value] of Object.entries(manifest.commandEnv ?? {})) {
2102
+ const resolved = resolveValue(manifest, value);
2103
+ if (resolved === void 0) {
2104
+ continue;
2105
+ }
2106
+ env[key] = resolved;
2107
+ }
2108
+ return env;
2109
+ }
2110
+
2026
2111
  // src/chat/plugins/auth/api-headers-broker.ts
2027
2112
  import { randomUUID } from "crypto";
2028
2113
  var MAX_LEASE_MS = 60 * 60 * 1e3;
2029
- var ENV_PLACEHOLDER_RE2 = /\$\{([A-Z_][A-Z0-9_]*)\}/g;
2114
+ var ENV_PLACEHOLDER_RE3 = /\$\{([A-Z_][A-Z0-9_]*)\}/g;
2030
2115
  function resolveHeaders(provider, headers) {
2031
2116
  return Object.fromEntries(
2032
2117
  Object.entries(headers).map(([key, value]) => {
2033
- const resolved = value.replace(ENV_PLACEHOLDER_RE2, (_match, name) => {
2118
+ const resolved = value.replace(ENV_PLACEHOLDER_RE3, (_match, name) => {
2034
2119
  const envName = name;
2035
2120
  const envValue = process.env[envName]?.trim();
2036
2121
  if (!envValue) {
@@ -2045,12 +2130,12 @@ function resolveHeaders(provider, headers) {
2045
2130
  );
2046
2131
  }
2047
2132
  function resolveApiHeaderTransforms(manifest) {
2048
- const { apiDomains, apiHeaders } = manifest;
2049
- if (!apiDomains || !apiHeaders) {
2133
+ const { domains, apiHeaders } = manifest;
2134
+ if (!domains || !apiHeaders) {
2050
2135
  return [];
2051
2136
  }
2052
2137
  const resolvedHeaders = resolveHeaders(manifest.name, apiHeaders);
2053
- return apiDomains.map((domain) => ({
2138
+ return domains.map((domain) => ({
2054
2139
  domain,
2055
2140
  headers: resolvedHeaders
2056
2141
  }));
@@ -2066,7 +2151,7 @@ function createApiHeadersBroker(manifest) {
2066
2151
  return {
2067
2152
  id: randomUUID(),
2068
2153
  provider,
2069
- env: { ...manifest.commandEnv ?? {} },
2154
+ env: resolvePluginCommandEnv(manifest),
2070
2155
  headerTransforms,
2071
2156
  expiresAt: new Date(Date.now() + MAX_LEASE_MS).toISOString(),
2072
2157
  metadata: {
@@ -2145,6 +2230,13 @@ function createAppJwt(appId, privateKeyEnv) {
2145
2230
  const signature = signer.sign(getPrivateKey(privateKeyEnv)).toString("base64").replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
2146
2231
  return `${signingInput}.${signature}`;
2147
2232
  }
2233
+ function resolveAppId(appIdEnv) {
2234
+ const appId = process.env[appIdEnv]?.trim();
2235
+ if (!appId) {
2236
+ throw new Error(`Missing ${appIdEnv}`);
2237
+ }
2238
+ return appId;
2239
+ }
2148
2240
  async function githubRequest(apiBase, path3, params) {
2149
2241
  const response = await fetch(`${apiBase}${path3}`, {
2150
2242
  method: params.method ?? "GET",
@@ -2171,6 +2263,16 @@ async function githubRequest(apiBase, path3, params) {
2171
2263
  }
2172
2264
  return parsed;
2173
2265
  }
2266
+ function resolveGitHubApiDomain(credentials) {
2267
+ const apiDomain = credentials.domains.find((domain) => {
2268
+ const normalizedDomain = domain.toLowerCase();
2269
+ return normalizedDomain === "api.github.com" || normalizedDomain.startsWith("api.");
2270
+ });
2271
+ if (!apiDomain) {
2272
+ throw new Error("GitHub App provider requires an API domain");
2273
+ }
2274
+ return apiDomain;
2275
+ }
2174
2276
  var KNOWN_SCOPES = /* @__PURE__ */ new Set([
2175
2277
  "actions",
2176
2278
  "administration",
@@ -2220,37 +2322,60 @@ function capabilitiesToPermissions(capabilities, pluginName) {
2220
2322
  return permissions;
2221
2323
  }
2222
2324
  function createGitHubAppBroker(manifest, credentials) {
2223
- const tokenCache = /* @__PURE__ */ new Map();
2224
2325
  const provider = manifest.name;
2225
2326
  const {
2226
- apiDomains,
2327
+ domains,
2227
2328
  apiHeaders,
2228
2329
  authTokenEnv,
2229
2330
  appIdEnv,
2230
2331
  privateKeyEnv,
2231
2332
  installationIdEnv
2232
2333
  } = credentials;
2233
- const apiBase = `https://${apiDomains[0]}`;
2334
+ const apiDomain = resolveGitHubApiDomain(credentials);
2335
+ const apiBase = `https://${apiDomain}`;
2234
2336
  const placeholder = resolveAuthTokenPlaceholder(credentials);
2235
2337
  const pluginHeaderTransforms = () => resolveApiHeaderTransforms(manifest);
2236
- const GIT_DOMAIN = "github.com";
2237
- const GIT_CAPABILITIES = /* @__PURE__ */ new Set([
2238
- `${provider}.contents.read`,
2239
- `${provider}.contents.write`
2240
- ]);
2241
- const leaseDomains = manifest.capabilities.some(
2242
- (capability) => GIT_CAPABILITIES.has(capability)
2243
- ) ? [...apiDomains, GIT_DOMAIN] : apiDomains;
2338
+ const leaseDomains = [...new Set(domains)];
2244
2339
  function authorizationFor(domain, token) {
2245
- if (domain === GIT_DOMAIN) {
2340
+ if (isGitSmartHttpDomain(domain)) {
2246
2341
  return `Basic ${Buffer.from(`x-access-token:${token}`).toString("base64")}`;
2247
2342
  }
2248
2343
  return `Bearer ${token}`;
2249
2344
  }
2345
+ function isGitSmartHttpDomain(domain) {
2346
+ const normalizedDomain = domain.toLowerCase();
2347
+ const normalizedApiDomain = apiDomain.toLowerCase();
2348
+ return normalizedDomain === "github.com" || normalizedApiDomain.startsWith("api.") && normalizedDomain === normalizedApiDomain.slice("api.".length);
2349
+ }
2250
2350
  const permissions = capabilitiesToPermissions(
2251
2351
  manifest.capabilities,
2252
2352
  provider
2253
2353
  );
2354
+ function createLease(params) {
2355
+ return {
2356
+ id: randomUUID2(),
2357
+ provider,
2358
+ env: {
2359
+ ...resolvePluginCommandEnv(manifest),
2360
+ [authTokenEnv]: placeholder
2361
+ },
2362
+ headerTransforms: mergeHeaderTransforms([
2363
+ ...pluginHeaderTransforms(),
2364
+ ...leaseDomains.map((domain) => ({
2365
+ domain,
2366
+ headers: {
2367
+ ...apiHeaders ?? {},
2368
+ Authorization: authorizationFor(domain, params.token)
2369
+ }
2370
+ }))
2371
+ ]),
2372
+ expiresAt: new Date(params.expiresAtMs).toISOString(),
2373
+ metadata: {
2374
+ installationId: String(params.installationId),
2375
+ reason: params.reason
2376
+ }
2377
+ };
2378
+ }
2254
2379
  function resolveInstallationId() {
2255
2380
  const installationIdRaw = process.env[installationIdEnv]?.trim();
2256
2381
  if (!installationIdRaw) {
@@ -2265,38 +2390,10 @@ function createGitHubAppBroker(manifest, credentials) {
2265
2390
  return {
2266
2391
  async issue(input) {
2267
2392
  const installationId = resolveInstallationId();
2268
- const cacheKey = String(installationId);
2269
- const cached = tokenCache.get(cacheKey);
2270
- const now = Date.now();
2271
- if (cached && cached.expiresAt - now > 2 * 60 * 1e3) {
2272
- return {
2273
- id: randomUUID2(),
2274
- provider,
2275
- env: { ...manifest.commandEnv ?? {}, [authTokenEnv]: placeholder },
2276
- headerTransforms: mergeHeaderTransforms([
2277
- ...pluginHeaderTransforms(),
2278
- ...leaseDomains.map((domain) => ({
2279
- domain,
2280
- headers: {
2281
- ...apiHeaders ?? {},
2282
- Authorization: authorizationFor(domain, cached.token)
2283
- }
2284
- }))
2285
- ]),
2286
- expiresAt: new Date(cached.expiresAt).toISOString(),
2287
- metadata: {
2288
- installationId: String(cached.installationId),
2289
- reason: input.reason
2290
- }
2291
- };
2292
- }
2293
2393
  const tokenRequestBody = {
2294
2394
  permissions
2295
2395
  };
2296
- const appId = process.env[appIdEnv];
2297
- if (!appId) {
2298
- throw new Error(`Missing ${appIdEnv}`);
2299
- }
2396
+ const appId = resolveAppId(appIdEnv);
2300
2397
  const appJwt = createAppJwt(appId, privateKeyEnv);
2301
2398
  const accessTokenResponse = await githubRequest(apiBase, `/app/installations/${installationId}/access_tokens`, {
2302
2399
  method: "POST",
@@ -2308,34 +2405,12 @@ function createGitHubAppBroker(manifest, credentials) {
2308
2405
  providerExpiresAtMs,
2309
2406
  Date.now() + MAX_LEASE_MS2
2310
2407
  );
2311
- tokenCache.set(cacheKey, {
2408
+ return createLease({
2312
2409
  installationId,
2313
2410
  token: accessTokenResponse.token,
2314
- expiresAt: expiresAtMs
2411
+ expiresAtMs,
2412
+ reason: input.reason
2315
2413
  });
2316
- return {
2317
- id: randomUUID2(),
2318
- provider,
2319
- env: { ...manifest.commandEnv ?? {}, [authTokenEnv]: placeholder },
2320
- headerTransforms: mergeHeaderTransforms([
2321
- ...pluginHeaderTransforms(),
2322
- ...leaseDomains.map((domain) => ({
2323
- domain,
2324
- headers: {
2325
- ...apiHeaders ?? {},
2326
- Authorization: authorizationFor(
2327
- domain,
2328
- accessTokenResponse.token
2329
- )
2330
- }
2331
- }))
2332
- ]),
2333
- expiresAt: new Date(expiresAtMs).toISOString(),
2334
- metadata: {
2335
- installationId: String(installationId),
2336
- reason: input.reason
2337
- }
2338
- };
2339
2414
  }
2340
2415
  };
2341
2416
  }
@@ -2488,7 +2563,7 @@ function getLeaseExpiry(expiresAt) {
2488
2563
  }
2489
2564
  function createOAuthBearerBroker(manifest, credentials, deps) {
2490
2565
  const provider = manifest.name;
2491
- const { apiDomains, apiHeaders, authTokenEnv } = credentials;
2566
+ const { domains, apiHeaders, authTokenEnv } = credentials;
2492
2567
  const authTokenPlaceholder = resolveAuthTokenPlaceholder(credentials);
2493
2568
  const pluginHeaderTransforms = () => resolveApiHeaderTransforms(manifest);
2494
2569
  function buildLease(token, expiresAtMs, reason) {
@@ -2496,12 +2571,12 @@ function createOAuthBearerBroker(manifest, credentials, deps) {
2496
2571
  id: randomUUID3(),
2497
2572
  provider,
2498
2573
  env: {
2499
- ...manifest.commandEnv ?? {},
2574
+ ...resolvePluginCommandEnv(manifest),
2500
2575
  [authTokenEnv]: authTokenPlaceholder
2501
2576
  },
2502
2577
  headerTransforms: mergeHeaderTransforms([
2503
2578
  ...pluginHeaderTransforms(),
2504
- ...apiDomains.map((domain) => ({
2579
+ ...domains.map((domain) => ({
2505
2580
  domain,
2506
2581
  headers: { ...apiHeaders ?? {}, Authorization: `Bearer ${token}` }
2507
2582
  }))
@@ -2616,15 +2691,24 @@ function createLoadedPluginState(signature) {
2616
2691
  signature,
2617
2692
  pluginDefinitions: [],
2618
2693
  capabilityToPlugin: /* @__PURE__ */ new Map(),
2694
+ domainToPlugin: /* @__PURE__ */ new Map(),
2619
2695
  pluginConfigKeys: /* @__PURE__ */ new Set(),
2620
2696
  pluginsByName: /* @__PURE__ */ new Map(),
2621
2697
  packageSkillRoots: /* @__PURE__ */ new Set()
2622
2698
  };
2623
2699
  }
2700
+ function providerDomains(manifest) {
2701
+ return [
2702
+ .../* @__PURE__ */ new Set([
2703
+ ...manifest.credentials?.domains ?? [],
2704
+ ...manifest.domains ?? []
2705
+ ])
2706
+ ].sort((left, right) => left.localeCompare(right));
2707
+ }
2624
2708
  function registerPluginManifest(state, raw, pluginDir) {
2625
2709
  const manifest = parsePluginManifest(raw, pluginDir);
2626
2710
  if (state.pluginsByName.has(manifest.name)) {
2627
- return;
2711
+ throw new Error(`Duplicate plugin name "${manifest.name}"`);
2628
2712
  }
2629
2713
  for (const cap of manifest.capabilities) {
2630
2714
  if (state.capabilityToPlugin.has(cap)) {
@@ -2633,6 +2717,14 @@ function registerPluginManifest(state, raw, pluginDir) {
2633
2717
  );
2634
2718
  }
2635
2719
  }
2720
+ for (const domain of providerDomains(manifest)) {
2721
+ const owner = state.domainToPlugin.get(domain);
2722
+ if (owner) {
2723
+ throw new Error(
2724
+ `Duplicate provider domain "${domain}" in plugin "${manifest.name}" already declared by plugin "${owner}"`
2725
+ );
2726
+ }
2727
+ }
2636
2728
  const definition = {
2637
2729
  manifest,
2638
2730
  dir: pluginDir,
@@ -2646,6 +2738,9 @@ function registerPluginManifest(state, raw, pluginDir) {
2646
2738
  for (const key of manifest.configKeys) {
2647
2739
  state.pluginConfigKeys.add(key);
2648
2740
  }
2741
+ for (const domain of providerDomains(manifest)) {
2742
+ state.domainToPlugin.set(domain, manifest.name);
2743
+ }
2649
2744
  }
2650
2745
  function normalizePluginRoots(roots) {
2651
2746
  const resolved = [];
@@ -2967,6 +3062,7 @@ export {
2967
3062
  extractGenAiUsageSummary,
2968
3063
  extractGenAiUsageAttributes,
2969
3064
  mergeHeaderTransforms,
3065
+ resolvePluginCommandEnv,
2970
3066
  resolveAuthTokenPlaceholder,
2971
3067
  parsePluginManifest,
2972
3068
  CredentialUnavailableError,
@@ -7,7 +7,7 @@ import {
7
7
  serializeGenAiAttribute,
8
8
  setSpanAttributes,
9
9
  withSpan
10
- } from "./chunk-SCE5C645.js";
10
+ } from "./chunk-BCG3I2T2.js";
11
11
 
12
12
  // src/chat/state/adapter.ts
13
13
  import { createMemoryState } from "@chat-adapter/state-memory";
@@ -539,12 +539,13 @@ function buildNonInteractiveCommand(input) {
539
539
  ]
540
540
  };
541
541
  }
542
- async function runNonInteractiveCommand(runner, input) {
543
- return await runner.runCommand({
542
+ async function runNonInteractiveCommand(sandbox, input) {
543
+ const command = {
544
544
  ...buildNonInteractiveCommand(input),
545
545
  ...input.cwd ? { cwd: input.cwd } : {},
546
546
  ...input.sudo !== void 0 ? { sudo: input.sudo } : {}
547
- });
547
+ };
548
+ return await sandbox.runCommand(command);
548
549
  }
549
550
 
550
551
  // src/chat/sandbox/credentials.ts
@@ -558,6 +559,39 @@ function getVercelSandboxCredentials() {
558
559
  return void 0;
559
560
  }
560
561
 
562
+ // src/chat/sandbox/workspace.ts
563
+ function createSandboxInstance(sandbox) {
564
+ return {
565
+ sandboxId: sandbox.name,
566
+ fs: sandbox.fs,
567
+ extendTimeout(duration) {
568
+ return sandbox.extendTimeout(duration);
569
+ },
570
+ mkDir(path) {
571
+ return sandbox.mkDir(path);
572
+ },
573
+ readFileToBuffer(input) {
574
+ return sandbox.readFileToBuffer(input);
575
+ },
576
+ runCommand(input) {
577
+ return sandbox.runCommand(input);
578
+ },
579
+ async snapshot() {
580
+ const snapshot = await sandbox.snapshot();
581
+ return { snapshotId: snapshot.snapshotId };
582
+ },
583
+ stop() {
584
+ return sandbox.stop();
585
+ },
586
+ update(params) {
587
+ return sandbox.update(params);
588
+ },
589
+ writeFiles(files) {
590
+ return sandbox.writeFiles(files);
591
+ }
592
+ };
593
+ }
594
+
561
595
  // src/chat/sandbox/paths.ts
562
596
  function normalizeWorkspaceRoot(input) {
563
597
  const candidate = (input ?? "").trim();
@@ -906,11 +940,13 @@ async function createDependencySnapshot(profile, runtime, timeoutMs) {
906
940
  },
907
941
  async () => {
908
942
  const sandboxCredentials = getVercelSandboxCredentials();
909
- const sandbox = await Sandbox.create({
910
- timeout: timeoutMs,
911
- runtime,
912
- ...sandboxCredentials ?? {}
913
- });
943
+ const sandbox = createSandboxInstance(
944
+ await Sandbox.create({
945
+ timeout: timeoutMs,
946
+ runtime,
947
+ ...sandboxCredentials ?? {}
948
+ })
949
+ );
914
950
  try {
915
951
  await installRuntimeDependencies(sandbox, profile.dependencies);
916
952
  await runRuntimePostinstall(sandbox, profile.postinstall);
@@ -927,7 +963,7 @@ async function createDependencySnapshot(profile, runtime, timeoutMs) {
927
963
  );
928
964
  } finally {
929
965
  try {
930
- await sandbox.stop({ blocking: true });
966
+ await sandbox.stop();
931
967
  } catch {
932
968
  }
933
969
  }
@@ -1144,6 +1180,7 @@ export {
1144
1180
  buildNonInteractiveShellScript,
1145
1181
  runNonInteractiveCommand,
1146
1182
  getVercelSandboxCredentials,
1183
+ createSandboxInstance,
1147
1184
  getRuntimeDependencyProfileHash,
1148
1185
  resolveRuntimeDependencySnapshot,
1149
1186
  isSnapshotMissingError
@@ -2,7 +2,7 @@ import {
2
2
  getPluginForSkillPath,
3
3
  getPluginSkillRoots,
4
4
  logWarn
5
- } from "./chunk-SCE5C645.js";
5
+ } from "./chunk-BCG3I2T2.js";
6
6
  import {
7
7
  skillRoots
8
8
  } from "./chunk-XPXD3FCE.js";