@sentry/junior 0.202.0 → 0.204.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.
Files changed (174) hide show
  1. package/dist/agent-CMyTD4DD.js +17509 -0
  2. package/dist/agent-hooks-Bbcp9e33.js +4040 -0
  3. package/dist/agent-runner-DNb1A49M.js +678 -0
  4. package/dist/api/schema.d.ts +2728 -2639
  5. package/dist/api/schema.js +4 -169
  6. package/dist/api.d.ts +23 -17
  7. package/dist/api.js +3881 -5326
  8. package/dist/app-B1TOqBI8.d.ts +246 -0
  9. package/dist/app.d.ts +4 -520
  10. package/dist/app.js +9522 -11875
  11. package/dist/auth-pause-state-DosEalIG.js +168 -0
  12. package/dist/catalog-runtime-BReos-D4.js +7 -0
  13. package/dist/checkpoint-CNrPyRJN.js +2450 -0
  14. package/dist/cli/chat.d.ts +20 -0
  15. package/dist/cli/chat.js +229 -336
  16. package/dist/cli/check.d.ts +8 -0
  17. package/dist/cli/check.js +513 -806
  18. package/dist/cli/env.d.ts +13 -0
  19. package/dist/cli/env.js +63 -12
  20. package/dist/cli/init.d.ts +3 -0
  21. package/dist/cli/init.js +90 -159
  22. package/dist/cli/main.d.ts +6 -0
  23. package/dist/cli/main.js +43 -64
  24. package/dist/cli/plugins.d.ts +13 -0
  25. package/dist/cli/plugins.js +148 -227
  26. package/dist/cli/run.d.ts +16 -0
  27. package/dist/cli/run.js +52 -8
  28. package/dist/cli/snapshot-create.d.ts +4 -0
  29. package/dist/cli/snapshot-create.js +71 -125
  30. package/dist/cli/upgrade.d.ts +15 -0
  31. package/dist/cli/upgrade.js +232 -335
  32. package/dist/config-nGCW1OTG.js +941 -0
  33. package/dist/context-B45_WqVX.js +298 -0
  34. package/dist/conversation-privacy-B4voFRSm.js +182 -0
  35. package/dist/conversation-work-C6y3T5Hx.js +51 -0
  36. package/dist/credential-sync-kXr1hvO1.js +2660 -0
  37. package/dist/db-BeyXKJOI.d.ts +5 -0
  38. package/dist/destinations-BeBiJeEM.js +29 -0
  39. package/dist/execution-stats-DWfSihcH.js +373 -0
  40. package/dist/executor-DcMSlI5c.js +721 -0
  41. package/dist/experimental-CIXf7Uye.js +46 -0
  42. package/dist/history-ClE4toxe.d.ts +32 -0
  43. package/dist/history-CqTuSAdI.js +441 -0
  44. package/dist/instrumentation.d.ts +3 -3
  45. package/dist/instrumentation.js +130 -176
  46. package/dist/job-queue-Drn_LXlM.js +63 -0
  47. package/dist/logging-BzAk1S_s.js +1210 -0
  48. package/dist/model-profile-CZmVoGdX.js +49 -0
  49. package/dist/nitro.d.ts +31 -36
  50. package/dist/nitro.js +275 -456
  51. package/dist/oauth-callback-server-JiftokvA.js +130 -0
  52. package/dist/oauth-relay-CdCnu80W.js +62 -0
  53. package/dist/package-discovery-7a9YqLhE.js +455 -0
  54. package/dist/pending-auth-CWmSdZVg.js +185 -0
  55. package/dist/plugin-module-BKGOXca2.js +81 -0
  56. package/dist/plugins-BI9A1vKA.d.ts +95 -0
  57. package/dist/plugins-egKWjgue.js +99 -0
  58. package/dist/private-trace-result-BLvBjy_e.js +16 -0
  59. package/dist/profile-CrT5nXbw.js +861 -0
  60. package/dist/registry-DH1xUo2N.js +1419 -0
  61. package/dist/reporting-schema-DosTN__Q.js +105 -0
  62. package/dist/resolve-CZHnfxaS.js +546 -0
  63. package/dist/rolldown-runtime-B4FMCO8f.js +28 -0
  64. package/dist/runner-CZV2sC46.js +392 -0
  65. package/dist/sandbox-egress-signals-bHZfmhbd.js +46 -0
  66. package/dist/schema-Rn8-XEaN.js +1140 -0
  67. package/dist/sentry-CD3SdJl-.js +26 -0
  68. package/dist/skills-CIcDQSgM.js +316 -0
  69. package/dist/spawn-DdkUFRmq.js +29 -0
  70. package/dist/stats-DTUbAb88.js +2634 -0
  71. package/dist/store-C5qxF2ac.d.ts +208 -0
  72. package/dist/task-queue-CJIR4RF7.js +136 -0
  73. package/dist/task-runner-D-O3A-Cp.js +639 -0
  74. package/dist/thread-state-CRc9jTJS.js +431 -0
  75. package/dist/turn-execution-BkinIdJF.js +47 -0
  76. package/dist/turn-lifecycle-DQV19VTW.js +54 -0
  77. package/dist/usage-D3EbSO7w.js +296 -0
  78. package/dist/usage-schema-CKaDkH_j.js +20 -0
  79. package/dist/validation-C2CR-OX3.js +40 -0
  80. package/dist/vercel-queue-DIkVcfK4.js +167 -0
  81. package/dist/vercel.d.ts +6 -6
  82. package/dist/vercel.js +14 -6
  83. package/dist/version-CnXZ614E.d.ts +5 -0
  84. package/dist/version-DxcWdoKa.js +8 -0
  85. package/dist/version.d.ts +2 -4
  86. package/dist/version.js +2 -6
  87. package/dist/web-authorization-RMBxAR_t.js +76 -0
  88. package/dist/web-input-3EPfpf55.js +494 -0
  89. package/dist/work-fGofI7Op.js +509 -0
  90. package/dist/worker-CsOLq-mR.js +625 -0
  91. package/package.json +9 -9
  92. package/dist/agent-hooks-UXITLYOK.js +0 -51
  93. package/dist/catalog-runtime-BG3JLLK2.js +0 -14
  94. package/dist/chunk-2LYHWMR4.js +0 -46
  95. package/dist/chunk-2MK3QBGA.js +0 -321
  96. package/dist/chunk-3PGQSKJV.js +0 -692
  97. package/dist/chunk-3UI3WFTX.js +0 -519
  98. package/dist/chunk-3Y47VMHV.js +0 -6
  99. package/dist/chunk-4UO6FK4G.js +0 -64
  100. package/dist/chunk-5NOPO64K.js +0 -198
  101. package/dist/chunk-5PKWYPFT.js +0 -2943
  102. package/dist/chunk-63NKKFJL.js +0 -1228
  103. package/dist/chunk-6AYH6252.js +0 -787
  104. package/dist/chunk-6Y5B5PEX.js +0 -80
  105. package/dist/chunk-7DHZP5KJ.js +0 -333
  106. package/dist/chunk-7H2VZTWN.js +0 -103
  107. package/dist/chunk-7NL7T6VU.js +0 -406
  108. package/dist/chunk-A76OIMYN.js +0 -102
  109. package/dist/chunk-AWMS3SPP.js +0 -58
  110. package/dist/chunk-B3LY5RCL.js +0 -229
  111. package/dist/chunk-BD2KT3OB.js +0 -36
  112. package/dist/chunk-BXCX5QKA.js +0 -2086
  113. package/dist/chunk-C6VVF43U.js +0 -81
  114. package/dist/chunk-CEMMLV5L.js +0 -98
  115. package/dist/chunk-CM5HNV2W.js +0 -131
  116. package/dist/chunk-DA2KGZZI.js +0 -13
  117. package/dist/chunk-DBVY6HLX.js +0 -64
  118. package/dist/chunk-DIB4GX3G.js +0 -8
  119. package/dist/chunk-DKIJ2RP5.js +0 -68
  120. package/dist/chunk-DL2KLKVH.js +0 -931
  121. package/dist/chunk-DWPN6SCH.js +0 -172
  122. package/dist/chunk-ELWOYVOY.js +0 -206
  123. package/dist/chunk-EWT7H65X.js +0 -506
  124. package/dist/chunk-FA6POGLH.js +0 -70
  125. package/dist/chunk-HMREVIO2.js +0 -135
  126. package/dist/chunk-IIWUT4FO.js +0 -820
  127. package/dist/chunk-JAL3CH4G.js +0 -27
  128. package/dist/chunk-JSJQEWBJ.js +0 -3295
  129. package/dist/chunk-KHKKHDLD.js +0 -387
  130. package/dist/chunk-LA6R4A7Q.js +0 -706
  131. package/dist/chunk-LOOU2DUR.js +0 -20428
  132. package/dist/chunk-M6FHWXE3.js +0 -700
  133. package/dist/chunk-M76HMB7M.js +0 -1856
  134. package/dist/chunk-MAGNK5J4.js +0 -1520
  135. package/dist/chunk-MTM5IFCY.js +0 -1190
  136. package/dist/chunk-N7TMSOZH.js +0 -235
  137. package/dist/chunk-NHJU2ZAL.js +0 -56
  138. package/dist/chunk-NSXKRGRT.js +0 -717
  139. package/dist/chunk-OWMPFXJA.js +0 -1764
  140. package/dist/chunk-PDO5BLNM.js +0 -22
  141. package/dist/chunk-PEQXYSSE.js +0 -45
  142. package/dist/chunk-PW4P4JYF.js +0 -1135
  143. package/dist/chunk-QB54UQUS.js +0 -320
  144. package/dist/chunk-QP3OGJXF.js +0 -105
  145. package/dist/chunk-RC745GGZ.js +0 -122
  146. package/dist/chunk-REOIRYPR.js +0 -37
  147. package/dist/chunk-SCOZRLZ4.js +0 -164
  148. package/dist/chunk-TXJSXBV5.js +0 -1433
  149. package/dist/chunk-TXKUKMVO.js +0 -417
  150. package/dist/chunk-U2GPUYAL.js +0 -3656
  151. package/dist/chunk-UIE3R5XU.js +0 -17
  152. package/dist/chunk-UXPG6ZIN.js +0 -84
  153. package/dist/chunk-VFUK3X5B.js +0 -50
  154. package/dist/chunk-WQSHDTTI.js +0 -1163
  155. package/dist/chunk-X6GDDWQH.js +0 -60
  156. package/dist/chunk-Y2CM7HXH.js +0 -111
  157. package/dist/chunk-Y7X25LFY.js +0 -15
  158. package/dist/chunk-YDUR3ZYW.js +0 -10
  159. package/dist/conversation-work-VIOQ5ZF3.js +0 -54
  160. package/dist/db-3L2MCVRI.js +0 -26
  161. package/dist/discovery-DWVDM7UK.js +0 -44
  162. package/dist/experimental-P5FZM4CU.js +0 -12
  163. package/dist/oauth-callback-server-YVV6C6T2.js +0 -243
  164. package/dist/oauth-relay-5XPVRPYB.js +0 -8
  165. package/dist/plugins-YTYJMMJF.js +0 -14
  166. package/dist/runner-LQ7RETDP.js +0 -522
  167. package/dist/sandbox-egress-signals-FIL7EGPR.js +0 -73
  168. package/dist/sentry-MB3NL3IR.js +0 -42
  169. package/dist/skills-XRWYEDCD.js +0 -27
  170. package/dist/spawn-RC6SJUAI.js +0 -42
  171. package/dist/task-runner-7DY7ARYH.js +0 -36
  172. package/dist/validation-7MNQGCJA.js +0 -17
  173. package/dist/work-COALHUCG.js +0 -51
  174. package/dist/worker-6MURZIPD.js +0 -24
@@ -1,2086 +0,0 @@
1
- import {
2
- credentialUserSubjectId
3
- } from "./chunk-CEMMLV5L.js";
4
- import {
5
- discoverInstalledPluginPackageContent,
6
- normalizePluginPackageNames
7
- } from "./chunk-DWPN6SCH.js";
8
- import {
9
- pluginRoots
10
- } from "./chunk-TXKUKMVO.js";
11
- import {
12
- logInfo,
13
- logWarn,
14
- setSpanAttributes
15
- } from "./chunk-MAGNK5J4.js";
16
-
17
- // src/chat/plugins/manifest.ts
18
- import { z } from "zod";
19
- import { parse as parseYaml } from "yaml";
20
-
21
- // src/chat/plugins/inline-manifest-source.ts
22
- function setDefined(target, key, value) {
23
- if (value !== void 0) {
24
- target[key] = value;
25
- }
26
- }
27
- function isRecord(value) {
28
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
29
- }
30
- function unqualifyManifestToken(name, value) {
31
- if (typeof name === "string" && typeof value === "string" && value.startsWith(`${name}.`)) {
32
- return value.slice(name.length + 1);
33
- }
34
- return value;
35
- }
36
- function inlineTokenListSource(name, values) {
37
- if (values === void 0 || !Array.isArray(values)) {
38
- return values;
39
- }
40
- return values.map((value) => unqualifyManifestToken(name, value));
41
- }
42
- function inlineCredentialsSource(credentials) {
43
- if (credentials === void 0 || !isRecord(credentials)) {
44
- return credentials;
45
- }
46
- const result = {};
47
- setDefined(result, "type", credentials.type);
48
- setDefined(result, "domains", credentials.domains);
49
- setDefined(result, "api-headers", credentials.apiHeaders);
50
- setDefined(result, "auth-token-env", credentials.authTokenEnv);
51
- setDefined(
52
- result,
53
- "auth-token-placeholder",
54
- credentials.authTokenPlaceholder
55
- );
56
- return result;
57
- }
58
- function inlineMcpSource(mcp) {
59
- if (mcp === void 0 || !isRecord(mcp)) {
60
- return mcp;
61
- }
62
- const result = {};
63
- setDefined(result, "transport", mcp.transport);
64
- setDefined(result, "url", mcp.url);
65
- setDefined(result, "headers", mcp.headers);
66
- setDefined(result, "allowed-tools", mcp.allowedTools);
67
- setDefined(result, "wrapped-tools", mcp.wrappedTools);
68
- return result;
69
- }
70
- function inlineOauthSource(oauth) {
71
- if (oauth === void 0 || !isRecord(oauth)) {
72
- return oauth;
73
- }
74
- const result = {};
75
- setDefined(result, "client-id-env", oauth.clientIdEnv);
76
- setDefined(result, "client-secret-env", oauth.clientSecretEnv);
77
- setDefined(result, "authorize-endpoint", oauth.authorizeEndpoint);
78
- setDefined(result, "token-endpoint", oauth.tokenEndpoint);
79
- setDefined(result, "scope", oauth.scope);
80
- setDefined(result, "authorize-params", oauth.authorizeParams);
81
- setDefined(result, "token-auth-method", oauth.tokenAuthMethod);
82
- setDefined(result, "token-extra-headers", oauth.tokenExtraHeaders);
83
- setDefined(
84
- result,
85
- "treat-empty-scope-as-unreported",
86
- oauth.treatEmptyScopeAsUnreported
87
- );
88
- return result;
89
- }
90
- function inlineTargetSource(name, target) {
91
- if (target === void 0 || !isRecord(target)) {
92
- return target;
93
- }
94
- const result = {};
95
- setDefined(result, "type", target.type);
96
- setDefined(
97
- result,
98
- "config-key",
99
- unqualifyManifestToken(name, target.configKey)
100
- );
101
- setDefined(result, "command-flags", target.commandFlags);
102
- return result;
103
- }
104
- function inlineManifestSource(manifest) {
105
- const result = {};
106
- setDefined(result, "name", manifest.name);
107
- setDefined(result, "display-name", manifest.displayName);
108
- setDefined(result, "description", manifest.description);
109
- setDefined(
110
- result,
111
- "config-keys",
112
- inlineTokenListSource(manifest.name, manifest.configKeys)
113
- );
114
- setDefined(result, "domains", manifest.domains);
115
- setDefined(result, "api-headers", manifest.apiHeaders);
116
- setDefined(result, "command-env", manifest.commandEnv);
117
- setDefined(result, "env-vars", manifest.envVars);
118
- setDefined(
119
- result,
120
- "credentials",
121
- inlineCredentialsSource(manifest.credentials)
122
- );
123
- setDefined(result, "runtime-dependencies", manifest.runtimeDependencies);
124
- setDefined(result, "runtime-postinstall", manifest.runtimePostinstall);
125
- setDefined(result, "mcp", inlineMcpSource(manifest.mcp));
126
- setDefined(result, "oauth", inlineOauthSource(manifest.oauth));
127
- setDefined(
128
- result,
129
- "target",
130
- inlineTargetSource(manifest.name, manifest.target)
131
- );
132
- return result;
133
- }
134
-
135
- // src/chat/plugins/manifest.ts
136
- var PLUGIN_NAME_RE = /^[a-z][a-z0-9-]*$/;
137
- var SHORT_CONFIG_KEY_RE = /^[a-z0-9]+(\.[a-z0-9-]+)*$/;
138
- var TARGET_FLAG_RE = /^-{1,2}[A-Za-z0-9][A-Za-z0-9-]*$/;
139
- var AUTH_TOKEN_ENV_RE = /^[A-Z][A-Z0-9_]*$/;
140
- var ENV_VAR_NAME_RE = /^[A-Z_][A-Z0-9_]*$/;
141
- var ENV_PLACEHOLDER_RE = /\$\{([A-Z_][A-Z0-9_]*)\}/g;
142
- var API_DOMAIN_RE = /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
143
- var RUNTIME_POSTINSTALL_CMD_RE = /^[A-Za-z0-9._/-]+$/;
144
- var RESERVED_AUTHORIZE_PARAM_KEYS = /* @__PURE__ */ new Set([
145
- "client_id",
146
- "scope",
147
- "state",
148
- "redirect_uri",
149
- "response_type"
150
- ]);
151
- var FORBIDDEN_API_HEADER_NAMES = /* @__PURE__ */ new Set(["authorization"]);
152
- var FORBIDDEN_TOKEN_HEADER_NAMES = /* @__PURE__ */ new Set(["authorization"]);
153
- var trimmedString = z.string().transform((value) => value.trim());
154
- var nonEmptyTrimmedString = trimmedString.pipe(
155
- z.string().min(1, { error: "must be a non-empty string" })
156
- );
157
- var nonEmptyStringArraySchema = (fieldName, options = {}) => z.array(z.string(), {
158
- error: "must be an array of strings when provided"
159
- }).min(1, {
160
- error: options.nonEmptyMessage ?? "must be a non-empty array of strings when provided"
161
- }).transform((values, ctx) => {
162
- const result = [];
163
- const seen = /* @__PURE__ */ new Set();
164
- for (const rawValue of values) {
165
- const value = rawValue.trim();
166
- if (!value) {
167
- ctx.addIssue({
168
- code: z.ZodIssueCode.custom,
169
- message: `${fieldName} entries must be non-empty strings`
170
- });
171
- return z.NEVER;
172
- }
173
- if (seen.has(value)) {
174
- continue;
175
- }
176
- seen.add(value);
177
- result.push(value);
178
- }
179
- return result;
180
- });
181
- var envVarString = nonEmptyTrimmedString.refine(
182
- (value) => AUTH_TOKEN_ENV_RE.test(value),
183
- {
184
- error: "must be an uppercase env var name"
185
- }
186
- );
187
- var httpsUrlString = nonEmptyTrimmedString.superRefine((value, ctx) => {
188
- let parsed;
189
- try {
190
- parsed = new URL(value);
191
- } catch {
192
- ctx.addIssue({
193
- code: z.ZodIssueCode.custom,
194
- message: "must be a valid URL"
195
- });
196
- return;
197
- }
198
- if (parsed.protocol !== "https:") {
199
- ctx.addIssue({
200
- code: z.ZodIssueCode.custom,
201
- message: "must use https"
202
- });
203
- }
204
- });
205
- var stringMapSchema = z.record(z.string(), z.unknown()).transform((record, ctx) => {
206
- const entries = Object.entries(record);
207
- const result = {};
208
- const seen = /* @__PURE__ */ new Set();
209
- for (const [rawKey, rawValue] of entries) {
210
- const key = rawKey.trim();
211
- if (!key) {
212
- ctx.addIssue({
213
- code: z.ZodIssueCode.custom,
214
- message: "keys must be non-empty strings"
215
- });
216
- return z.NEVER;
217
- }
218
- if (typeof rawValue !== "string" || !rawValue.trim()) {
219
- ctx.addIssue({
220
- code: z.ZodIssueCode.custom,
221
- message: `${key} must be a non-empty string`
222
- });
223
- return z.NEVER;
224
- }
225
- const normalizedKey = key.toLowerCase();
226
- if (seen.has(normalizedKey)) {
227
- ctx.addIssue({
228
- code: z.ZodIssueCode.custom,
229
- message: `${key} is duplicated`
230
- });
231
- return z.NEVER;
232
- }
233
- seen.add(normalizedKey);
234
- result[key] = rawValue.trim();
235
- }
236
- return result;
237
- });
238
- var domainsSchema = z.array(z.unknown()).min(1, {
239
- error: "must be a non-empty array of strings"
240
- }).transform((domains, ctx) => {
241
- return domains.map((rawDomain) => {
242
- const domain = typeof rawDomain === "string" ? rawDomain.trim().toLowerCase() : "";
243
- if (!domain) {
244
- ctx.addIssue({
245
- code: z.ZodIssueCode.custom,
246
- message: "entries must be non-empty strings"
247
- });
248
- return z.NEVER;
249
- }
250
- if (!API_DOMAIN_RE.test(domain)) {
251
- ctx.addIssue({
252
- code: z.ZodIssueCode.custom,
253
- message: "entries must be valid domain names"
254
- });
255
- return z.NEVER;
256
- }
257
- return domain;
258
- });
259
- });
260
- var baseCredentialsSchema = z.object({
261
- domains: domainsSchema.optional()
262
- }).passthrough();
263
- var oauthBearerCredentialsSchema = baseCredentialsSchema.extend({
264
- "api-headers": stringMapSchema.optional(),
265
- "auth-token-env": envVarString,
266
- "auth-token-placeholder": nonEmptyTrimmedString.optional(),
267
- type: z.literal("oauth-bearer")
268
- });
269
- var runtimeDependencyEntrySchema = z.object({
270
- type: z.enum(["npm", "system"]),
271
- package: z.string().optional(),
272
- version: z.string().optional(),
273
- url: z.string().optional(),
274
- sha256: z.string().optional()
275
- }).passthrough();
276
- var runtimePostinstallCommandSourceSchema = z.object({
277
- cmd: nonEmptyTrimmedString,
278
- args: z.array(z.string(), {
279
- error: "args must be an array of strings when provided"
280
- }).optional(),
281
- sudo: z.boolean({
282
- error: "sudo must be a boolean when provided"
283
- }).optional()
284
- }).passthrough();
285
- var oauthSourceSchema = z.object({
286
- "client-id-env": envVarString,
287
- "client-secret-env": envVarString,
288
- "authorize-endpoint": httpsUrlString,
289
- "token-endpoint": httpsUrlString,
290
- scope: nonEmptyTrimmedString.optional(),
291
- "authorize-params": stringMapSchema.optional(),
292
- "token-extra-headers": stringMapSchema.optional(),
293
- "token-auth-method": nonEmptyTrimmedString.refine((value) => value === "body" || value === "basic", {
294
- error: 'must be "body" or "basic"'
295
- }).optional(),
296
- "treat-empty-scope-as-unreported": z.boolean().optional()
297
- }).passthrough();
298
- var mcpSourceSchema = z.object({
299
- transport: nonEmptyTrimmedString.refine((value) => value === "http", {
300
- error: 'must be "http"'
301
- }).optional(),
302
- url: httpsUrlString,
303
- headers: stringMapSchema.optional(),
304
- "allowed-tools": nonEmptyStringArraySchema("allowed-tools").optional(),
305
- "wrapped-tools": nonEmptyStringArraySchema("wrapped-tools").optional()
306
- }).passthrough();
307
- var targetSourceSchema = z.object({
308
- type: nonEmptyTrimmedString.refine((value) => PLUGIN_NAME_RE.test(value), {
309
- error: "type must be a lowercase target identifier"
310
- }),
311
- "config-key": nonEmptyTrimmedString,
312
- "command-flags": nonEmptyStringArraySchema("command-flags").optional()
313
- }).passthrough();
314
- var manifestSourceSchema = z.object({
315
- name: z.string().refine((value) => PLUGIN_NAME_RE.test(value), {
316
- error: "invalid"
317
- }),
318
- "display-name": nonEmptyTrimmedString,
319
- description: nonEmptyTrimmedString,
320
- "config-keys": z.array(z.string(), {
321
- error: "must be an array when provided"
322
- }).optional(),
323
- domains: domainsSchema.optional(),
324
- "api-headers": stringMapSchema.optional(),
325
- "command-env": stringMapSchema.optional(),
326
- credentials: z.record(z.string(), z.unknown(), {
327
- error: "must be an object when provided"
328
- }).optional(),
329
- "runtime-dependencies": z.array(z.unknown(), {
330
- error: "must be an array"
331
- }).optional(),
332
- "runtime-postinstall": z.array(z.unknown(), {
333
- error: "must be an array"
334
- }).optional(),
335
- "env-vars": z.record(z.string(), z.unknown(), {
336
- error: "must be an object"
337
- }).optional(),
338
- mcp: z.record(z.string(), z.unknown(), {
339
- error: "must be an object"
340
- }).optional(),
341
- oauth: z.record(z.string(), z.unknown(), {
342
- error: "must be an object"
343
- }).optional(),
344
- target: z.record(z.string(), z.unknown(), {
345
- error: "must be an object"
346
- }).optional()
347
- }).passthrough();
348
- function setDefined2(target, key, value) {
349
- if (value !== void 0) {
350
- target[key] = value;
351
- }
352
- }
353
- function manifestConfigPatch(config) {
354
- const result = {};
355
- setDefined2(result, "display-name", config.displayName);
356
- setDefined2(result, "description", config.description);
357
- setDefined2(result, "config-keys", config.configKeys);
358
- setDefined2(result, "domains", config.domains);
359
- setDefined2(result, "api-headers", config.apiHeaders);
360
- setDefined2(result, "command-env", config.commandEnv);
361
- setDefined2(result, "env-vars", config.envVars);
362
- if (config.credentials !== void 0) {
363
- if (!config.credentials) {
364
- result.credentials = null;
365
- } else {
366
- const credentials = {};
367
- setDefined2(credentials, "type", config.credentials.type);
368
- setDefined2(credentials, "domains", config.credentials.domains);
369
- setDefined2(credentials, "api-headers", config.credentials.apiHeaders);
370
- setDefined2(
371
- credentials,
372
- "auth-token-env",
373
- config.credentials.authTokenEnv
374
- );
375
- setDefined2(
376
- credentials,
377
- "auth-token-placeholder",
378
- config.credentials.authTokenPlaceholder
379
- );
380
- result.credentials = credentials;
381
- }
382
- }
383
- setDefined2(result, "runtime-dependencies", config.runtimeDependencies);
384
- setDefined2(result, "runtime-postinstall", config.runtimePostinstall);
385
- if (config.mcp !== void 0) {
386
- if (!config.mcp) {
387
- result.mcp = null;
388
- } else {
389
- const mcp = {};
390
- setDefined2(mcp, "transport", config.mcp.transport);
391
- setDefined2(mcp, "url", config.mcp.url);
392
- setDefined2(mcp, "headers", config.mcp.headers);
393
- setDefined2(mcp, "allowed-tools", config.mcp.allowedTools);
394
- setDefined2(mcp, "wrapped-tools", config.mcp.wrappedTools);
395
- result.mcp = mcp;
396
- }
397
- }
398
- if (config.oauth !== void 0) {
399
- if (!config.oauth) {
400
- result.oauth = null;
401
- } else {
402
- const oauth = {};
403
- setDefined2(oauth, "client-id-env", config.oauth.clientIdEnv);
404
- setDefined2(oauth, "client-secret-env", config.oauth.clientSecretEnv);
405
- setDefined2(oauth, "authorize-endpoint", config.oauth.authorizeEndpoint);
406
- setDefined2(oauth, "token-endpoint", config.oauth.tokenEndpoint);
407
- setDefined2(oauth, "scope", config.oauth.scope);
408
- setDefined2(oauth, "authorize-params", config.oauth.authorizeParams);
409
- setDefined2(oauth, "token-auth-method", config.oauth.tokenAuthMethod);
410
- setDefined2(oauth, "token-extra-headers", config.oauth.tokenExtraHeaders);
411
- setDefined2(
412
- oauth,
413
- "treat-empty-scope-as-unreported",
414
- config.oauth.treatEmptyScopeAsUnreported
415
- );
416
- result.oauth = oauth;
417
- }
418
- }
419
- if (config.target !== void 0) {
420
- if (!config.target) {
421
- result.target = null;
422
- } else {
423
- const target = {};
424
- setDefined2(target, "type", config.target.type);
425
- setDefined2(target, "config-key", config.target.configKey);
426
- setDefined2(target, "command-flags", config.target.commandFlags);
427
- result.target = target;
428
- }
429
- }
430
- return result;
431
- }
432
- function isPlainRecord(value) {
433
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
434
- }
435
- function mergeManifestConfig(base, patch, options = {}) {
436
- const merged = { ...base };
437
- for (const [key, value] of Object.entries(patch)) {
438
- if (options.root && key === "name") {
439
- throw new Error("plugins.manifests cannot change plugin names");
440
- }
441
- if (value === null) {
442
- delete merged[key];
443
- continue;
444
- }
445
- const current = merged[key];
446
- if (isPlainRecord(current) && isPlainRecord(value)) {
447
- merged[key] = mergeManifestConfig(current, value);
448
- continue;
449
- }
450
- merged[key] = value;
451
- }
452
- return merged;
453
- }
454
- function applyManifestConfig(source, config) {
455
- const name = source.name;
456
- if (typeof name !== "string") {
457
- return source;
458
- }
459
- const manifestConfig = config?.manifests?.[name];
460
- if (manifestConfig && "name" in manifestConfig) {
461
- throw new Error("plugins.manifests cannot change plugin names");
462
- }
463
- return manifestConfig ? mergeManifestConfig(source, manifestConfigPatch(manifestConfig), {
464
- root: true
465
- }) : source;
466
- }
467
- function formatPath(path2) {
468
- return path2.map((segment) => String(segment)).join(".");
469
- }
470
- function issueMessage(error, prefix) {
471
- const issue = error.issues[0];
472
- if (!issue) {
473
- return prefix;
474
- }
475
- const suffix = formatPath(issue.path);
476
- return suffix ? `${prefix}.${suffix} ${issue.message}` : `${prefix} ${issue.message}`;
477
- }
478
- function normalizeStringMap(value, prefix, options = {}) {
479
- if (!value) {
480
- return void 0;
481
- }
482
- const keys = Object.keys(value);
483
- if (keys.length === 0) {
484
- return void 0;
485
- }
486
- for (const key of keys) {
487
- const normalizedKey = key.toLowerCase();
488
- if (options.reservedKeys?.has(normalizedKey)) {
489
- throw new Error(`${prefix}.${key} is reserved by the runtime`);
490
- }
491
- if (options.forbiddenKeys?.has(normalizedKey)) {
492
- throw new Error(`${prefix}.${key} is not allowed`);
493
- }
494
- }
495
- return value;
496
- }
497
- function envReferences(value) {
498
- return Array.from(value.matchAll(ENV_PLACEHOLDER_RE), (match) => {
499
- return match[1];
500
- });
501
- }
502
- function assertDeclaredEnvReferences(value, envVars, context) {
503
- for (const name of envReferences(value)) {
504
- if (!Object.prototype.hasOwnProperty.call(envVars, name)) {
505
- throw new Error(
506
- `${context} references env var ${name} which is not declared in env-vars`
507
- );
508
- }
509
- if (envVars[name]?.default !== void 0) {
510
- throw new Error(
511
- `${context} references env var ${name}, but API header env vars must not declare defaults`
512
- );
513
- }
514
- }
515
- }
516
- function normalizeRequiredApiHeaders(value, prefix, envVars) {
517
- const apiHeaders = normalizeStringMap(value, prefix);
518
- if (!apiHeaders) {
519
- throw new Error(`${prefix} must contain at least one header`);
520
- }
521
- for (const [key, headerValue] of Object.entries(apiHeaders)) {
522
- assertDeclaredEnvReferences(headerValue, envVars, `${prefix}.${key}`);
523
- }
524
- return apiHeaders;
525
- }
526
- function assertCommandEnvReferencesDeclared(value, envVars, context) {
527
- for (const name of envReferences(value)) {
528
- if (!Object.prototype.hasOwnProperty.call(envVars, name)) {
529
- throw new Error(
530
- `${context} references env var ${name} which is not declared in env-vars`
531
- );
532
- }
533
- }
534
- }
535
- function expandCommandEnvPlaceholders(template, envVars, context) {
536
- return template.replace(ENV_PLACEHOLDER_RE, (match, name) => {
537
- const varName = name;
538
- const declaration = envVars[varName];
539
- if (declaration?.default === void 0) {
540
- return match;
541
- }
542
- return expandEnvPlaceholders(match, envVars, context);
543
- });
544
- }
545
- function normalizeCommandEnv(value, prefix, envVars) {
546
- const env = normalizeStringMap(value, prefix);
547
- if (!env) {
548
- throw new Error(`${prefix} must contain at least one env var`);
549
- }
550
- for (const [key, envValue] of Object.entries(env)) {
551
- if (!ENV_VAR_NAME_RE.test(key)) {
552
- throw new Error(`${prefix}.${key} must be an uppercase env var name`);
553
- }
554
- assertCommandEnvReferencesDeclared(envValue, envVars, `${prefix}.${key}`);
555
- }
556
- return Object.fromEntries(
557
- Object.entries(env).map(([key, envValue]) => [
558
- key,
559
- expandCommandEnvPlaceholders(envValue, envVars, `${prefix}.${key}`)
560
- ])
561
- );
562
- }
563
- function assertCommandEnvDoesNotExposeHostSecretRefs(commandEnv, apiHeaders, credentials, oauth, pluginName) {
564
- if (!commandEnv) {
565
- return;
566
- }
567
- const hostOnlyRefs = /* @__PURE__ */ new Set();
568
- for (const value of Object.values(apiHeaders ?? {})) {
569
- for (const name of envReferences(value)) {
570
- hostOnlyRefs.add(name);
571
- }
572
- }
573
- if (credentials) {
574
- if (credentials.authTokenEnv) {
575
- hostOnlyRefs.add(credentials.authTokenEnv);
576
- }
577
- }
578
- if (oauth) {
579
- hostOnlyRefs.add(oauth.clientIdEnv);
580
- hostOnlyRefs.add(oauth.clientSecretEnv);
581
- }
582
- for (const [key, value] of Object.entries(commandEnv)) {
583
- for (const name of envReferences(value)) {
584
- if (hostOnlyRefs.has(name)) {
585
- throw new Error(
586
- `Plugin ${pluginName} command-env.${key} references env var ${name}, but credential/API header env vars must stay host-only`
587
- );
588
- }
589
- }
590
- }
591
- }
592
- function assertCommandEnvHostRefsAreExplicitlyExposed(commandEnv, envVars, pluginName) {
593
- if (!commandEnv) {
594
- return;
595
- }
596
- for (const [key, value] of Object.entries(commandEnv)) {
597
- for (const name of envReferences(value)) {
598
- const declaration = envVars[name];
599
- if (declaration && declaration.default === void 0 && declaration.exposeToCommandEnv !== true) {
600
- throw new Error(
601
- `Plugin ${pluginName} command-env.${key} references env var ${name}, but env-vars.${name} must set expose-to-command-env: true before host env can be exposed to sandbox`
602
- );
603
- }
604
- }
605
- }
606
- }
607
- function normalizeCredentials(data, name) {
608
- if (data.type !== "oauth-bearer") {
609
- throw new Error(
610
- `Plugin ${name} has unsupported credentials.type: "${String(data.type)}"`
611
- );
612
- }
613
- const result = oauthBearerCredentialsSchema.safeParse(data);
614
- if (!result.success) {
615
- throw new Error(issueMessage(result.error, `Plugin ${name} credentials`));
616
- }
617
- if (!result.data.domains) {
618
- throw new Error(`Plugin ${name} credentials requires domains`);
619
- }
620
- const domains = result.data.domains;
621
- const apiHeaders = result.data["api-headers"] ? normalizeStringMap(
622
- result.data["api-headers"],
623
- `Plugin ${name} credentials.api-headers`,
624
- { forbiddenKeys: FORBIDDEN_API_HEADER_NAMES }
625
- ) : void 0;
626
- return {
627
- type: "oauth-bearer",
628
- domains,
629
- ...apiHeaders ? { apiHeaders } : void 0,
630
- authTokenEnv: result.data["auth-token-env"],
631
- ...result.data["auth-token-placeholder"] ? { authTokenPlaceholder: result.data["auth-token-placeholder"] } : void 0
632
- };
633
- }
634
- function normalizeRuntimeDependencies(entries, name) {
635
- const parsed = [];
636
- const seen = /* @__PURE__ */ new Set();
637
- for (const entry of entries) {
638
- const result = runtimeDependencyEntrySchema.safeParse(entry);
639
- if (!result.success) {
640
- throw new Error(
641
- issueMessage(
642
- result.error,
643
- `Plugin ${name} runtime-dependencies entries`
644
- )
645
- );
646
- }
647
- const record = result.data;
648
- const packageName = typeof record.package === "string" ? record.package.trim() : "";
649
- const packageUrl = typeof record.url === "string" ? record.url.trim() : "";
650
- const version = record.version;
651
- const sha256 = record.sha256;
652
- if (record.type === "npm") {
653
- if (!packageName) {
654
- throw new Error(
655
- `Plugin ${name} runtime dependency package must be a non-empty string`
656
- );
657
- }
658
- if (record.url !== void 0 || sha256 !== void 0) {
659
- throw new Error(
660
- `Plugin ${name} npm runtime dependencies must only include package/version fields`
661
- );
662
- }
663
- const normalizedVersion = typeof version === "string" ? version.trim() : "latest";
664
- if (!normalizedVersion) {
665
- throw new Error(
666
- `Plugin ${name} runtime dependency version must be a non-empty string when provided`
667
- );
668
- }
669
- const dedupeKey2 = `${record.type}:${packageName}:${normalizedVersion}`;
670
- if (seen.has(dedupeKey2)) {
671
- continue;
672
- }
673
- seen.add(dedupeKey2);
674
- parsed.push({
675
- type: "npm",
676
- package: packageName,
677
- version: normalizedVersion
678
- });
679
- continue;
680
- }
681
- if (version !== void 0) {
682
- throw new Error(
683
- `Plugin ${name} system runtime dependencies must not include a version`
684
- );
685
- }
686
- if (packageName && packageUrl) {
687
- throw new Error(
688
- `Plugin ${name} system runtime dependencies must specify either package or url, not both`
689
- );
690
- }
691
- if (!packageName && !packageUrl) {
692
- throw new Error(
693
- `Plugin ${name} system runtime dependencies must specify package or url`
694
- );
695
- }
696
- if (packageName) {
697
- if (sha256 !== void 0) {
698
- throw new Error(
699
- `Plugin ${name} system runtime dependency package entries must not include sha256`
700
- );
701
- }
702
- const dedupeKey2 = `${record.type}:package:${packageName}`;
703
- if (seen.has(dedupeKey2)) {
704
- continue;
705
- }
706
- seen.add(dedupeKey2);
707
- parsed.push({
708
- type: "system",
709
- package: packageName
710
- });
711
- continue;
712
- }
713
- if (!/^https:\/\//i.test(packageUrl)) {
714
- throw new Error(
715
- `Plugin ${name} system runtime dependency url must be an https URL`
716
- );
717
- }
718
- const normalizedSha256 = typeof sha256 === "string" ? sha256.trim().toLowerCase() : "";
719
- if (!/^[a-f0-9]{64}$/.test(normalizedSha256)) {
720
- throw new Error(
721
- `Plugin ${name} system runtime dependency url entries must include a valid sha256`
722
- );
723
- }
724
- const dedupeKey = `${record.type}:url:${packageUrl}:${normalizedSha256}`;
725
- if (seen.has(dedupeKey)) {
726
- continue;
727
- }
728
- seen.add(dedupeKey);
729
- parsed.push({
730
- type: "system",
731
- url: packageUrl,
732
- sha256: normalizedSha256
733
- });
734
- }
735
- return parsed.length > 0 ? parsed : void 0;
736
- }
737
- function normalizeRuntimePostinstall(commands, name) {
738
- const parsed = [];
739
- for (const command of commands) {
740
- const result = runtimePostinstallCommandSourceSchema.safeParse(command);
741
- if (!result.success) {
742
- throw new Error(
743
- issueMessage(result.error, `Plugin ${name} runtime-postinstall`)
744
- );
745
- }
746
- if (!RUNTIME_POSTINSTALL_CMD_RE.test(result.data.cmd)) {
747
- throw new Error(
748
- `Plugin ${name} runtime-postinstall cmd must be a single executable token (letters, digits, ., _, /, -)`
749
- );
750
- }
751
- const normalizedArgs = result.data.args?.map((arg) => arg.trim()).filter((arg) => arg.length > 0);
752
- parsed.push({
753
- cmd: result.data.cmd,
754
- ...normalizedArgs && normalizedArgs.length > 0 ? { args: normalizedArgs } : void 0,
755
- ...typeof result.data.sudo === "boolean" ? { sudo: result.data.sudo } : void 0
756
- });
757
- }
758
- return parsed.length > 0 ? parsed : void 0;
759
- }
760
- var envVarDeclarationSchema = z.preprocess(
761
- (value) => value === null || value === void 0 ? {} : value,
762
- z.object({
763
- default: z.string().optional(),
764
- "expose-to-command-env": z.boolean().optional(),
765
- exposeToCommandEnv: z.boolean().optional()
766
- }).strict()
767
- );
768
- function normalizeEnvVars(data, pluginName) {
769
- const normalized = {};
770
- for (const [rawName, rawDecl] of Object.entries(data)) {
771
- const name = rawName.trim();
772
- if (!ENV_VAR_NAME_RE.test(name)) {
773
- throw new Error(
774
- `Plugin ${pluginName} env-vars key "${rawName}" must match [A-Z_][A-Z0-9_]*`
775
- );
776
- }
777
- const parsed = envVarDeclarationSchema.safeParse(rawDecl);
778
- if (!parsed.success) {
779
- throw new Error(
780
- issueMessage(parsed.error, `Plugin ${pluginName} env-vars.${name}`)
781
- );
782
- }
783
- const decl = {};
784
- if (parsed.data.default !== void 0) {
785
- decl.default = parsed.data.default;
786
- }
787
- if (parsed.data["expose-to-command-env"] === true || parsed.data.exposeToCommandEnv === true) {
788
- decl.exposeToCommandEnv = true;
789
- }
790
- normalized[name] = decl;
791
- }
792
- return normalized;
793
- }
794
- function expandEnvPlaceholders(template, envVars, context) {
795
- return template.replace(ENV_PLACEHOLDER_RE, (_match, name) => {
796
- const varName = name;
797
- if (!Object.prototype.hasOwnProperty.call(envVars, varName)) {
798
- throw new Error(
799
- `${context} references env var ${varName} which is not declared in env-vars`
800
- );
801
- }
802
- const decl = envVars[varName];
803
- const fromProcess = process.env[varName];
804
- if (fromProcess !== void 0 && fromProcess !== "") {
805
- return fromProcess;
806
- }
807
- if (decl.default !== void 0) {
808
- return decl.default;
809
- }
810
- throw new Error(
811
- `${context} env var ${varName} is unset and has no default in env-vars`
812
- );
813
- });
814
- }
815
- function normalizeMcp(data, envVars, name) {
816
- const prepared = { ...data };
817
- if (typeof prepared.url === "string") {
818
- prepared.url = expandEnvPlaceholders(
819
- prepared.url,
820
- envVars,
821
- `Plugin ${name} mcp.url`
822
- );
823
- }
824
- const result = mcpSourceSchema.safeParse(prepared);
825
- if (!result.success) {
826
- throw new Error(issueMessage(result.error, `Plugin ${name} mcp`));
827
- }
828
- const headers = result.data.headers ? normalizeStringMap(result.data.headers, `Plugin ${name} mcp.headers`, {
829
- forbiddenKeys: FORBIDDEN_API_HEADER_NAMES
830
- }) : void 0;
831
- return {
832
- transport: "http",
833
- url: result.data.url,
834
- ...headers ? { headers } : void 0,
835
- ...result.data["allowed-tools"] ? { allowedTools: result.data["allowed-tools"] } : void 0,
836
- ...result.data["wrapped-tools"] ? { wrappedTools: result.data["wrapped-tools"] } : void 0
837
- };
838
- }
839
- function parseManifestSource(parsedSource, dir, config, options) {
840
- const source = applyManifestConfig(parsedSource, config);
841
- const sourceResult = manifestSourceSchema.safeParse(source);
842
- if (!sourceResult.success) {
843
- const issue = sourceResult.error.issues[0];
844
- const path2 = formatPath(issue?.path ?? []);
845
- if (path2 === "name") {
846
- throw new Error(`Invalid plugin name in ${dir}: "${parsedSource.name}"`);
847
- }
848
- if (path2 === "description") {
849
- throw new Error(`Invalid plugin description in ${dir}`);
850
- }
851
- if (path2 === "config-keys") {
852
- throw new Error(
853
- `Plugin ${String(parsedSource.name ?? "unknown")} config-keys must be an array when provided`
854
- );
855
- }
856
- if (path2 === "domains") {
857
- throw new Error(
858
- `Plugin ${String(parsedSource.name ?? "unknown")} ${path2} must be a non-empty array of domains`
859
- );
860
- }
861
- if (path2 === "api-headers") {
862
- throw new Error(
863
- `Plugin ${String(parsedSource.name ?? "unknown")} api-headers must be an object when provided`
864
- );
865
- }
866
- if (path2 === "command-env") {
867
- throw new Error(
868
- `Plugin ${String(parsedSource.name ?? "unknown")} command-env must be an object when provided`
869
- );
870
- }
871
- if (path2 === "credentials") {
872
- throw new Error(
873
- `Plugin ${String(parsedSource.name ?? "unknown")} credentials must be an object when provided`
874
- );
875
- }
876
- if (path2 === "runtime-dependencies") {
877
- throw new Error(
878
- `Plugin ${String(parsedSource.name ?? "unknown")} runtime-dependencies must be an array`
879
- );
880
- }
881
- if (path2 === "runtime-postinstall") {
882
- throw new Error(
883
- `Plugin ${String(parsedSource.name ?? "unknown")} runtime-postinstall must be an array`
884
- );
885
- }
886
- if (path2 === "env-vars") {
887
- throw new Error(
888
- `Plugin ${String(parsedSource.name ?? "unknown")} env-vars must be an object`
889
- );
890
- }
891
- if (path2 === "mcp") {
892
- throw new Error(
893
- `Plugin ${String(parsedSource.name ?? "unknown")} mcp must be an object`
894
- );
895
- }
896
- if (path2 === "oauth") {
897
- throw new Error(
898
- `Plugin ${String(parsedSource.name ?? "unknown")} oauth must be an object`
899
- );
900
- }
901
- if (path2 === "target") {
902
- throw new Error(
903
- `Plugin ${String(parsedSource.name ?? "unknown")} target must be an object`
904
- );
905
- }
906
- throw new Error(issue?.message ?? `Invalid plugin manifest in ${dir}`);
907
- }
908
- const data = sourceResult.data;
909
- const configKeys = (data["config-keys"] ?? []).map((key) => {
910
- if (!SHORT_CONFIG_KEY_RE.test(key)) {
911
- throw new Error(`Invalid config key "${key}" in plugin ${data.name}`);
912
- }
913
- return `${data.name}.${key}`;
914
- });
915
- const envVars = data["env-vars"] ? normalizeEnvVars(data["env-vars"], data.name) : {};
916
- const apiHeaders = data["api-headers"] ? normalizeRequiredApiHeaders(
917
- data["api-headers"],
918
- `Plugin ${data.name} api-headers`,
919
- envVars
920
- ) : void 0;
921
- const domains = data.domains;
922
- if (apiHeaders && !domains) {
923
- throw new Error(`Plugin ${data.name} api-headers requires domains`);
924
- }
925
- if (domains && !apiHeaders && !data.credentials && options?.allowHookManagedEgress !== true) {
926
- throw new Error(
927
- `Plugin ${data.name} domains requires credentials or api-headers`
928
- );
929
- }
930
- const commandEnv = data["command-env"] ? normalizeCommandEnv(
931
- data["command-env"],
932
- `Plugin ${data.name} command-env`,
933
- envVars
934
- ) : void 0;
935
- const credentials = data.credentials ? normalizeCredentials(data.credentials, data.name) : void 0;
936
- const runtimeDependencies = data["runtime-dependencies"] ? normalizeRuntimeDependencies(data["runtime-dependencies"], data.name) : void 0;
937
- const runtimePostinstall = data["runtime-postinstall"] ? normalizeRuntimePostinstall(data["runtime-postinstall"], data.name) : void 0;
938
- const mcp = data.mcp ? normalizeMcp(data.mcp, envVars, data.name) : void 0;
939
- const manifest = {
940
- name: data.name,
941
- displayName: data["display-name"],
942
- description: data.description,
943
- configKeys,
944
- ...domains ? { domains } : void 0,
945
- ...apiHeaders ? { apiHeaders } : void 0,
946
- ...commandEnv ? { commandEnv } : void 0,
947
- ...Object.keys(envVars).length > 0 ? { envVars } : void 0,
948
- ...credentials ? { credentials } : void 0,
949
- ...runtimeDependencies ? { runtimeDependencies } : void 0,
950
- ...runtimePostinstall ? { runtimePostinstall } : void 0,
951
- ...mcp ? { mcp } : void 0
952
- };
953
- if (data.oauth) {
954
- if (!credentials && options?.allowHookManagedEgress !== true) {
955
- throw new Error(`Plugin ${data.name} oauth requires credentials`);
956
- }
957
- const result = oauthSourceSchema.safeParse(data.oauth);
958
- if (!result.success) {
959
- throw new Error(issueMessage(result.error, `Plugin ${data.name} oauth`));
960
- }
961
- const authorizeParams = result.data["authorize-params"] ? normalizeStringMap(
962
- result.data["authorize-params"],
963
- `Plugin ${data.name} oauth.authorize-params`,
964
- {
965
- reservedKeys: RESERVED_AUTHORIZE_PARAM_KEYS
966
- }
967
- ) : void 0;
968
- const tokenExtraHeaders = result.data["token-extra-headers"] ? normalizeStringMap(
969
- result.data["token-extra-headers"],
970
- `Plugin ${data.name} oauth.token-extra-headers`,
971
- {
972
- forbiddenKeys: FORBIDDEN_TOKEN_HEADER_NAMES
973
- }
974
- ) : void 0;
975
- manifest.oauth = {
976
- clientIdEnv: result.data["client-id-env"],
977
- clientSecretEnv: result.data["client-secret-env"],
978
- authorizeEndpoint: result.data["authorize-endpoint"],
979
- tokenEndpoint: result.data["token-endpoint"],
980
- ...result.data.scope ? { scope: result.data.scope } : void 0,
981
- ...authorizeParams ? { authorizeParams } : void 0,
982
- ...result.data["token-auth-method"] ? { tokenAuthMethod: result.data["token-auth-method"] } : void 0,
983
- ...tokenExtraHeaders ? { tokenExtraHeaders } : void 0,
984
- ...result.data["treat-empty-scope-as-unreported"] ? { treatEmptyScopeAsUnreported: true } : void 0
985
- };
986
- }
987
- assertCommandEnvDoesNotExposeHostSecretRefs(
988
- data["command-env"],
989
- apiHeaders,
990
- credentials,
991
- manifest.oauth,
992
- data.name
993
- );
994
- assertCommandEnvHostRefsAreExplicitlyExposed(
995
- data["command-env"],
996
- envVars,
997
- data.name
998
- );
999
- if (data.target) {
1000
- const result = targetSourceSchema.safeParse(data.target);
1001
- if (!result.success) {
1002
- throw new Error(issueMessage(result.error, `Plugin ${data.name} target`));
1003
- }
1004
- if (!SHORT_CONFIG_KEY_RE.test(result.data["config-key"])) {
1005
- throw new Error(
1006
- `Plugin ${data.name} target.config-key "${result.data["config-key"]}" is invalid`
1007
- );
1008
- }
1009
- const qualifiedKey = `${data.name}.${result.data["config-key"]}`;
1010
- if (!configKeys.includes(qualifiedKey)) {
1011
- throw new Error(
1012
- `Plugin ${data.name} target.config-key "${result.data["config-key"]}" must be listed in config-keys`
1013
- );
1014
- }
1015
- const commandFlags = result.data["command-flags"];
1016
- if (commandFlags && commandFlags.some((flag) => !TARGET_FLAG_RE.test(flag))) {
1017
- throw new Error(
1018
- `Plugin ${data.name} target.command-flags must contain CLI flags like --repo or -R`
1019
- );
1020
- }
1021
- manifest.target = {
1022
- type: result.data.type,
1023
- configKey: qualifiedKey,
1024
- ...commandFlags ? { commandFlags } : void 0
1025
- };
1026
- }
1027
- return manifest;
1028
- }
1029
- function parsePluginManifest(raw, dir, config) {
1030
- let parsedYaml;
1031
- try {
1032
- parsedYaml = parseYaml(raw);
1033
- } catch (error) {
1034
- throw new Error(
1035
- `Invalid plugin manifest in ${dir}: ${error instanceof Error ? error.message : String(error)}`
1036
- );
1037
- }
1038
- if (!parsedYaml || typeof parsedYaml !== "object" || Array.isArray(parsedYaml)) {
1039
- throw new Error(`Invalid plugin manifest in ${dir}: expected an object`);
1040
- }
1041
- return parseManifestSource(parsedYaml, dir, config);
1042
- }
1043
- function parseInlinePluginManifest(manifest, dir, config) {
1044
- return parseManifestSource(inlineManifestSource(manifest), dir, config, {
1045
- allowHookManagedEgress: true
1046
- });
1047
- }
1048
-
1049
- // src/chat/plugins/registry.ts
1050
- import { readFileSync, readdirSync, statSync } from "fs";
1051
- import path from "path";
1052
-
1053
- // src/chat/plugins/auth/oauth-bearer-broker.ts
1054
- import { randomUUID as randomUUID2 } from "crypto";
1055
-
1056
- // src/chat/credentials/broker.ts
1057
- var CredentialUnavailableError = class extends Error {
1058
- provider;
1059
- constructor(provider, message) {
1060
- super(message);
1061
- this.name = "CredentialUnavailableError";
1062
- this.provider = provider;
1063
- }
1064
- };
1065
-
1066
- // src/chat/credentials/header-transforms.ts
1067
- function mergeHeaderTransforms(transforms) {
1068
- const byDomain = /* @__PURE__ */ new Map();
1069
- for (const transform of transforms) {
1070
- byDomain.set(transform.domain, {
1071
- ...byDomain.get(transform.domain) ?? {},
1072
- ...transform.headers
1073
- });
1074
- }
1075
- return [...byDomain.entries()].map(([domain, headers]) => ({
1076
- domain,
1077
- headers
1078
- }));
1079
- }
1080
-
1081
- // src/chat/credentials/oauth-scope.ts
1082
- function parseScope(scope) {
1083
- if (!scope) {
1084
- return [];
1085
- }
1086
- return [...new Set(scope.split(/\s+/).filter(Boolean))].sort();
1087
- }
1088
- function normalizeOAuthScope(scope) {
1089
- const parsed = parseScope(scope);
1090
- return parsed.length > 0 ? parsed.join(" ") : void 0;
1091
- }
1092
- function hasRequiredOAuthScope(storedScope, requiredScope) {
1093
- const required = parseScope(requiredScope);
1094
- if (required.length === 0) {
1095
- return true;
1096
- }
1097
- const stored = new Set(parseScope(storedScope));
1098
- if (stored.size === 0) {
1099
- return false;
1100
- }
1101
- return required.every((scope) => stored.has(scope));
1102
- }
1103
-
1104
- // src/chat/oauth-response.ts
1105
- var MAX_OAUTH_ERROR_BODY_BYTES = 16 * 1024;
1106
- var MAX_OAUTH_ERROR_BODY_READS = MAX_OAUTH_ERROR_BODY_BYTES;
1107
- var MAX_OAUTH_ERROR_BODY_READ_MS = 5e3;
1108
- var OAUTH_ERROR_BODY_READ_TIMEOUT = /* @__PURE__ */ Symbol("oauth-error-body-read-timeout");
1109
- async function readOAuthErrorBodyChunk(reader) {
1110
- let timeout;
1111
- const timeoutResult = new Promise(
1112
- (resolve) => {
1113
- timeout = setTimeout(
1114
- resolve,
1115
- MAX_OAUTH_ERROR_BODY_READ_MS,
1116
- OAUTH_ERROR_BODY_READ_TIMEOUT
1117
- );
1118
- }
1119
- );
1120
- try {
1121
- const result = await Promise.race([reader.read(), timeoutResult]);
1122
- if (result === OAUTH_ERROR_BODY_READ_TIMEOUT) {
1123
- await reader.cancel();
1124
- throw new Error("OAuth error body read timed out");
1125
- }
1126
- return result;
1127
- } finally {
1128
- if (timeout) {
1129
- clearTimeout(timeout);
1130
- }
1131
- }
1132
- }
1133
- var OAuthProviderError = class extends Error {
1134
- phase;
1135
- provider;
1136
- resourceHost;
1137
- status;
1138
- constructor(details) {
1139
- super(`OAuth provider ${details.phase.replace("_", " ")} failed`);
1140
- this.name = "OAuthProviderError";
1141
- this.phase = details.phase;
1142
- this.provider = details.provider;
1143
- this.resourceHost = details.resourceHost;
1144
- this.status = details.status;
1145
- }
1146
- };
1147
- function getOAuthProviderErrorAttributes(error) {
1148
- if (!(error instanceof OAuthProviderError)) {
1149
- return {};
1150
- }
1151
- return {
1152
- "app.credential.provider": error.provider,
1153
- "app.oauth.error.phase": error.phase,
1154
- "server.address": error.resourceHost,
1155
- ...error.status !== void 0 ? { "http.response.status_code": error.status } : void 0
1156
- };
1157
- }
1158
- async function readBoundedOAuthErrorBody(response) {
1159
- if (!response.body) {
1160
- return "";
1161
- }
1162
- const reader = response.body.getReader();
1163
- const decoder = new TextDecoder();
1164
- let bytesRead = 0;
1165
- let reads = 0;
1166
- let text = "";
1167
- try {
1168
- while (bytesRead < MAX_OAUTH_ERROR_BODY_BYTES && reads < MAX_OAUTH_ERROR_BODY_READS) {
1169
- reads += 1;
1170
- const { done, value } = await readOAuthErrorBodyChunk(reader);
1171
- if (done) {
1172
- return text + decoder.decode();
1173
- }
1174
- const remaining = MAX_OAUTH_ERROR_BODY_BYTES - bytesRead;
1175
- const chunk = value.subarray(0, remaining);
1176
- bytesRead += chunk.byteLength;
1177
- text += decoder.decode(chunk, { stream: true });
1178
- if (chunk.byteLength < value.byteLength) {
1179
- await reader.cancel();
1180
- return text + decoder.decode();
1181
- }
1182
- }
1183
- await reader.cancel();
1184
- return text + decoder.decode();
1185
- } finally {
1186
- reader.releaseLock();
1187
- }
1188
- }
1189
- function fetchWithBoundedOAuthErrorBodies(fetchFn = globalThis.fetch, onErrorResponseStatus) {
1190
- return (async (input, init) => {
1191
- const response = await fetchFn(input, init);
1192
- if (response.ok) {
1193
- return response;
1194
- }
1195
- onErrorResponseStatus?.(response.status);
1196
- if (!response.body) {
1197
- return response;
1198
- }
1199
- let body = "";
1200
- try {
1201
- body = await readBoundedOAuthErrorBody(response);
1202
- } catch {
1203
- await response.body.cancel().catch(() => void 0);
1204
- }
1205
- return new Response(body, {
1206
- headers: response.headers,
1207
- status: response.status,
1208
- statusText: response.statusText
1209
- });
1210
- });
1211
- }
1212
-
1213
- // src/chat/plugins/command-env.ts
1214
- var ENV_PLACEHOLDER_RE2 = /\$\{([A-Z_][A-Z0-9_]*)\}/g;
1215
- function resolveValue(manifest, value) {
1216
- let missing = false;
1217
- const resolved = value.replace(ENV_PLACEHOLDER_RE2, (match, name) => {
1218
- const envName = name;
1219
- const declaration = manifest.envVars?.[envName];
1220
- if (!declaration || declaration.default !== void 0) {
1221
- return match;
1222
- }
1223
- const hostValue = process.env[envName];
1224
- if (hostValue === void 0 || hostValue === "") {
1225
- missing = true;
1226
- return "";
1227
- }
1228
- return hostValue;
1229
- });
1230
- return missing ? void 0 : resolved;
1231
- }
1232
- function resolvePluginCommandEnv(manifest) {
1233
- const env = {};
1234
- for (const [key, value] of Object.entries(manifest.commandEnv ?? {})) {
1235
- const resolved = resolveValue(manifest, value);
1236
- if (resolved === void 0) {
1237
- continue;
1238
- }
1239
- env[key] = resolved;
1240
- }
1241
- return env;
1242
- }
1243
-
1244
- // src/chat/plugins/auth/auth-token-placeholder.ts
1245
- var DEFAULT_PLACEHOLDERS = {
1246
- "oauth-bearer": "host_managed_credential"
1247
- };
1248
- function resolveAuthTokenPlaceholder(credentials) {
1249
- return credentials.authTokenPlaceholder?.trim() || DEFAULT_PLACEHOLDERS[credentials.type];
1250
- }
1251
-
1252
- // src/chat/plugins/auth/api-headers-broker.ts
1253
- import { randomUUID } from "crypto";
1254
- var MAX_LEASE_MS = 60 * 60 * 1e3;
1255
- var ENV_PLACEHOLDER_RE3 = /\$\{([A-Z_][A-Z0-9_]*)\}/g;
1256
- function resolveHeaders(provider, headers) {
1257
- return Object.fromEntries(
1258
- Object.entries(headers).map(([key, value]) => {
1259
- const resolved = value.replace(ENV_PLACEHOLDER_RE3, (_match, name) => {
1260
- const envName = name;
1261
- const envValue = process.env[envName]?.trim();
1262
- if (!envValue) {
1263
- throw new Error(
1264
- `Missing ${envName} for API header provider "${provider}"`
1265
- );
1266
- }
1267
- return envValue;
1268
- });
1269
- return [key, resolved];
1270
- })
1271
- );
1272
- }
1273
- function resolveApiHeaderTransforms(manifest) {
1274
- const { domains, apiHeaders } = manifest;
1275
- if (!domains || !apiHeaders) {
1276
- return [];
1277
- }
1278
- const resolvedHeaders = resolveHeaders(manifest.name, apiHeaders);
1279
- return domains.map((domain) => ({
1280
- domain,
1281
- headers: resolvedHeaders
1282
- }));
1283
- }
1284
- function createApiHeadersBroker(manifest) {
1285
- const provider = manifest.name;
1286
- return {
1287
- async issue(input) {
1288
- const headerTransforms = resolveApiHeaderTransforms(manifest);
1289
- if (headerTransforms.length === 0) {
1290
- throw new Error(`No API headers configured for plugin "${provider}"`);
1291
- }
1292
- return {
1293
- id: randomUUID(),
1294
- provider,
1295
- env: resolvePluginCommandEnv(manifest),
1296
- headerTransforms,
1297
- expiresAt: new Date(Date.now() + MAX_LEASE_MS).toISOString(),
1298
- metadata: {
1299
- reason: input.reason
1300
- }
1301
- };
1302
- }
1303
- };
1304
- }
1305
-
1306
- // src/chat/plugins/auth/oauth-request.ts
1307
- import { z as z2 } from "zod";
1308
- var DEFAULT_TOKEN_CONTENT_TYPE = "application/x-www-form-urlencoded";
1309
- function requireNonEmptyTokenField(data, field) {
1310
- const value = data[field];
1311
- if (typeof value !== "string" || !value.trim()) {
1312
- throw new Error(`OAuth token response missing ${field}`);
1313
- }
1314
- return value;
1315
- }
1316
- function requireTokenResponseObject(data) {
1317
- if (!data || typeof data !== "object" || Array.isArray(data)) {
1318
- throw new Error("OAuth token response must be an object");
1319
- }
1320
- return data;
1321
- }
1322
- var parsedOAuthTokenResponseSchema = z2.object({
1323
- accessToken: z2.string().min(1),
1324
- refreshToken: z2.string().min(1),
1325
- expiresAt: z2.number().positive().optional(),
1326
- refreshTokenExpiresAt: z2.number().positive().optional(),
1327
- scope: z2.string().min(1).optional()
1328
- }).strict();
1329
- function contentTypeToBody(contentType, payload) {
1330
- const mediaType = contentType.split(";", 1)[0]?.trim().toLowerCase();
1331
- if (!mediaType || mediaType === DEFAULT_TOKEN_CONTENT_TYPE) {
1332
- return new URLSearchParams(payload);
1333
- }
1334
- if (mediaType === "application/json" || mediaType.endsWith("+json")) {
1335
- return JSON.stringify(payload);
1336
- }
1337
- throw new Error(`Unsupported OAuth token Content-Type: ${contentType}`);
1338
- }
1339
- function buildOAuthTokenRequest(input) {
1340
- const headers = new Headers({ Accept: "application/json" });
1341
- for (const [name, value] of Object.entries(input.tokenExtraHeaders ?? {})) {
1342
- headers.set(name, value);
1343
- }
1344
- if (!headers.has("Content-Type")) {
1345
- headers.set("Content-Type", DEFAULT_TOKEN_CONTENT_TYPE);
1346
- }
1347
- const payload = { ...input.payload };
1348
- if (input.tokenAuthMethod === "basic") {
1349
- headers.set(
1350
- "Authorization",
1351
- `Basic ${Buffer.from(`${input.clientId}:${input.clientSecret}`).toString("base64")}`
1352
- );
1353
- } else {
1354
- payload.client_id = input.clientId;
1355
- payload.client_secret = input.clientSecret;
1356
- }
1357
- const contentType = headers.get("Content-Type") ?? DEFAULT_TOKEN_CONTENT_TYPE;
1358
- const serializedHeaders = {};
1359
- headers.forEach((value, key) => {
1360
- serializedHeaders[key] = value;
1361
- });
1362
- return {
1363
- headers: serializedHeaders,
1364
- body: contentTypeToBody(contentType, payload)
1365
- };
1366
- }
1367
- function parseOAuthTokenResponse(data, requestedScope, options) {
1368
- const response = requireTokenResponseObject(data);
1369
- const accessToken = requireNonEmptyTokenField(response, "access_token");
1370
- const resolvedRefreshToken = response.refresh_token === void 0 ? options?.refreshToken : requireNonEmptyTokenField(response, "refresh_token");
1371
- if (!resolvedRefreshToken) {
1372
- throw new Error("OAuth token response missing refresh_token");
1373
- }
1374
- const expiresIn = response.expires_in;
1375
- const refreshTokenExpiresIn = response.refresh_token_expires_in;
1376
- const responseScope = response.scope;
1377
- let scope;
1378
- if (responseScope !== void 0) {
1379
- if (typeof responseScope !== "string") {
1380
- throw new Error("OAuth token response returned invalid scope");
1381
- }
1382
- const normalized = normalizeOAuthScope(responseScope);
1383
- if (normalized !== void 0) {
1384
- scope = normalized;
1385
- } else if (options?.treatEmptyScopeAsUnreported) {
1386
- scope = normalizeOAuthScope(requestedScope);
1387
- } else {
1388
- throw new Error("OAuth token response returned empty scope");
1389
- }
1390
- } else {
1391
- scope = normalizeOAuthScope(requestedScope);
1392
- }
1393
- const result = {
1394
- accessToken,
1395
- refreshToken: resolvedRefreshToken,
1396
- ...scope ? { scope } : void 0
1397
- };
1398
- if (expiresIn !== void 0) {
1399
- if (typeof expiresIn !== "number" || !Number.isFinite(expiresIn) || expiresIn <= 0) {
1400
- throw new Error("OAuth token response returned invalid expires_in");
1401
- }
1402
- result.expiresAt = Date.now() + expiresIn * 1e3;
1403
- }
1404
- if (refreshTokenExpiresIn !== void 0) {
1405
- if (typeof refreshTokenExpiresIn !== "number" || !Number.isFinite(refreshTokenExpiresIn) || refreshTokenExpiresIn <= 0) {
1406
- throw new Error(
1407
- "OAuth token response returned invalid refresh_token_expires_in"
1408
- );
1409
- }
1410
- result.refreshTokenExpiresAt = Date.now() + refreshTokenExpiresIn * 1e3;
1411
- }
1412
- return parsedOAuthTokenResponseSchema.parse(result);
1413
- }
1414
-
1415
- // src/chat/plugins/auth/oauth-bearer-broker.ts
1416
- var MAX_LEASE_MS2 = 60 * 60 * 1e3;
1417
- var REFRESH_BUFFER_MS = 5 * 60 * 1e3;
1418
- var TOKEN_REFRESH_TIMEOUT_MS = 2e4;
1419
- var OAuthRefreshRejectedError = class extends Error {
1420
- constructor(message) {
1421
- super(message);
1422
- this.name = "OAuthRefreshRejectedError";
1423
- }
1424
- };
1425
- function parseRefreshError(text) {
1426
- if (!text.trim()) {
1427
- return void 0;
1428
- }
1429
- try {
1430
- const parsed = JSON.parse(text);
1431
- return parsed && typeof parsed === "object" && !Array.isArray(parsed) && typeof parsed.error === "string" ? parsed.error : void 0;
1432
- } catch {
1433
- return void 0;
1434
- }
1435
- }
1436
- async function refreshAccessToken(provider, refreshToken, oauth, requestedScope) {
1437
- const clientId = process.env[oauth.clientIdEnv]?.trim();
1438
- const clientSecret = process.env[oauth.clientSecretEnv]?.trim();
1439
- if (!clientId || !clientSecret) {
1440
- throw new Error(
1441
- `Missing ${oauth.clientIdEnv} or ${oauth.clientSecretEnv} for token refresh`
1442
- );
1443
- }
1444
- const request = buildOAuthTokenRequest({
1445
- clientId,
1446
- clientSecret,
1447
- payload: {
1448
- grant_type: "refresh_token",
1449
- refresh_token: refreshToken
1450
- },
1451
- tokenAuthMethod: oauth.tokenAuthMethod,
1452
- tokenExtraHeaders: oauth.tokenExtraHeaders
1453
- });
1454
- const errorDetails = {
1455
- phase: "token_refresh",
1456
- provider,
1457
- resourceHost: new URL(oauth.tokenEndpoint).hostname
1458
- };
1459
- let response;
1460
- try {
1461
- response = await fetch(oauth.tokenEndpoint, {
1462
- method: "POST",
1463
- headers: request.headers,
1464
- body: request.body,
1465
- signal: AbortSignal.timeout(TOKEN_REFRESH_TIMEOUT_MS)
1466
- });
1467
- } catch {
1468
- throw new OAuthProviderError(errorDetails);
1469
- }
1470
- if (!response.ok) {
1471
- let errorCode;
1472
- try {
1473
- errorCode = parseRefreshError(await readBoundedOAuthErrorBody(response));
1474
- } catch {
1475
- throw new OAuthProviderError({
1476
- ...errorDetails,
1477
- status: response.status
1478
- });
1479
- }
1480
- if (errorCode === "invalid_grant" || errorCode === "bad_refresh_token") {
1481
- throw new OAuthRefreshRejectedError("Token refresh was rejected");
1482
- }
1483
- throw new OAuthProviderError({
1484
- ...errorDetails,
1485
- status: response.status
1486
- });
1487
- }
1488
- try {
1489
- const data = await response.json();
1490
- return parseOAuthTokenResponse(data, requestedScope, {
1491
- treatEmptyScopeAsUnreported: oauth.treatEmptyScopeAsUnreported,
1492
- refreshToken
1493
- });
1494
- } catch {
1495
- throw new OAuthProviderError(errorDetails);
1496
- }
1497
- }
1498
- function getLeaseExpiry(expiresAt) {
1499
- return expiresAt ? Math.min(expiresAt, Date.now() + MAX_LEASE_MS2) : Date.now() + MAX_LEASE_MS2;
1500
- }
1501
- function canUseStoredToken(stored) {
1502
- return stored !== void 0 && (stored.expiresAt === void 0 || stored.expiresAt > Date.now());
1503
- }
1504
- function shouldRefreshStoredToken(stored) {
1505
- return stored !== void 0 && stored.expiresAt !== void 0 && stored.expiresAt - Date.now() < REFRESH_BUFFER_MS;
1506
- }
1507
- function createOAuthBearerBroker(manifest, credentials, deps) {
1508
- const provider = manifest.name;
1509
- const { domains, apiHeaders, authTokenEnv } = credentials;
1510
- const authTokenPlaceholder = resolveAuthTokenPlaceholder(credentials);
1511
- const pluginHeaderTransforms = () => resolveApiHeaderTransforms(manifest);
1512
- function buildLease(token, expiresAtMs, reason) {
1513
- return {
1514
- id: randomUUID2(),
1515
- provider,
1516
- env: {
1517
- ...resolvePluginCommandEnv(manifest),
1518
- [authTokenEnv]: authTokenPlaceholder
1519
- },
1520
- headerTransforms: mergeHeaderTransforms([
1521
- ...pluginHeaderTransforms(),
1522
- ...domains.map((domain) => ({
1523
- domain,
1524
- headers: { ...apiHeaders ?? {}, Authorization: `Bearer ${token}` }
1525
- }))
1526
- ]),
1527
- expiresAt: new Date(expiresAtMs).toISOString(),
1528
- metadata: { reason }
1529
- };
1530
- }
1531
- return {
1532
- async issue(input) {
1533
- const envToken = process.env[authTokenEnv]?.trim();
1534
- const oauth = manifest.oauth;
1535
- const userSubjectId = credentialUserSubjectId(input.context);
1536
- if (!oauth) {
1537
- if (envToken) {
1538
- return buildLease(envToken, Date.now() + MAX_LEASE_MS2, input.reason);
1539
- }
1540
- throw new CredentialUnavailableError(
1541
- provider,
1542
- `No ${provider} credentials available.`
1543
- );
1544
- }
1545
- if (userSubjectId) {
1546
- const stored = await deps.userTokenStore.get(userSubjectId, provider);
1547
- if (stored) {
1548
- if (!hasRequiredOAuthScope(stored.scope, oauth.scope)) {
1549
- throw new CredentialUnavailableError(
1550
- provider,
1551
- `Your ${provider} connection needs to be reauthorized.`
1552
- );
1553
- }
1554
- if (shouldRefreshStoredToken(stored)) {
1555
- try {
1556
- return await deps.userTokenStore.withRefresh(
1557
- userSubjectId,
1558
- provider,
1559
- async () => {
1560
- const latest = await deps.userTokenStore.get(
1561
- userSubjectId,
1562
- provider
1563
- );
1564
- if (latest && !hasRequiredOAuthScope(latest.scope, oauth.scope)) {
1565
- throw new CredentialUnavailableError(
1566
- provider,
1567
- `Your ${provider} connection needs to be reauthorized.`
1568
- );
1569
- }
1570
- if (!shouldRefreshStoredToken(latest) && canUseStoredToken(latest)) {
1571
- return buildLease(
1572
- latest.accessToken,
1573
- getLeaseExpiry(latest.expiresAt),
1574
- input.reason
1575
- );
1576
- }
1577
- if (!latest) {
1578
- throw new CredentialUnavailableError(
1579
- provider,
1580
- `No ${provider} credentials available.`
1581
- );
1582
- }
1583
- const refreshed = await refreshAccessToken(
1584
- provider,
1585
- latest.refreshToken,
1586
- oauth,
1587
- latest.scope ?? oauth.scope
1588
- );
1589
- if (!hasRequiredOAuthScope(refreshed.scope, oauth.scope)) {
1590
- throw new CredentialUnavailableError(
1591
- provider,
1592
- `Your ${provider} connection needs to be reauthorized.`
1593
- );
1594
- }
1595
- const refreshedTokens = {
1596
- ...latest.refreshTokenExpiresAt ? { refreshTokenExpiresAt: latest.refreshTokenExpiresAt } : void 0,
1597
- ...refreshed,
1598
- ...latest.account ? { account: latest.account } : void 0
1599
- };
1600
- await deps.userTokenStore.set(
1601
- userSubjectId,
1602
- provider,
1603
- refreshedTokens
1604
- );
1605
- return buildLease(
1606
- refreshed.accessToken,
1607
- getLeaseExpiry(refreshed.expiresAt),
1608
- input.reason
1609
- );
1610
- }
1611
- );
1612
- } catch (error) {
1613
- if (error instanceof CredentialUnavailableError) {
1614
- throw error;
1615
- }
1616
- if (error instanceof OAuthRefreshRejectedError) {
1617
- throw new CredentialUnavailableError(
1618
- provider,
1619
- `Your ${provider} connection has expired.`
1620
- );
1621
- }
1622
- throw error;
1623
- }
1624
- }
1625
- if (canUseStoredToken(stored)) {
1626
- return buildLease(
1627
- stored.accessToken,
1628
- getLeaseExpiry(stored.expiresAt),
1629
- input.reason
1630
- );
1631
- }
1632
- throw new CredentialUnavailableError(
1633
- provider,
1634
- `Your ${provider} connection has expired.`
1635
- );
1636
- }
1637
- throw new CredentialUnavailableError(
1638
- provider,
1639
- `No ${provider} credentials available.`
1640
- );
1641
- }
1642
- if (envToken) {
1643
- return buildLease(envToken, getLeaseExpiry(), input.reason);
1644
- }
1645
- throw new CredentialUnavailableError(
1646
- provider,
1647
- `No ${provider} credentials available.`
1648
- );
1649
- }
1650
- };
1651
- }
1652
-
1653
- // src/chat/plugins/registry.ts
1654
- function getLoggedPluginNames() {
1655
- const globalState = globalThis;
1656
- globalState.__juniorLoggedPluginNames ??= /* @__PURE__ */ new Set();
1657
- return globalState.__juniorLoggedPluginNames;
1658
- }
1659
- function createLoadedPluginState(signature) {
1660
- return {
1661
- signature,
1662
- pluginDefinitions: [],
1663
- pluginMigrationRoots: /* @__PURE__ */ new Map(),
1664
- domainToPlugin: /* @__PURE__ */ new Map(),
1665
- pluginConfigKeys: /* @__PURE__ */ new Set(),
1666
- pluginsByName: /* @__PURE__ */ new Map(),
1667
- packageSkillRoots: /* @__PURE__ */ new Set()
1668
- };
1669
- }
1670
- function providerDomains(manifest) {
1671
- return [
1672
- .../* @__PURE__ */ new Set([
1673
- ...manifest.credentials?.domains ?? [],
1674
- ...manifest.domains ?? []
1675
- ])
1676
- ].sort((left, right) => left.localeCompare(right));
1677
- }
1678
- function registerPluginManifest(state, manifest, pluginDir, skillsDir, migrationsDir) {
1679
- if (state.pluginsByName.has(manifest.name)) {
1680
- throw new Error(`Duplicate plugin name "${manifest.name}"`);
1681
- }
1682
- for (const domain of providerDomains(manifest)) {
1683
- const owner = state.domainToPlugin.get(domain);
1684
- if (owner) {
1685
- throw new Error(
1686
- `Duplicate provider domain "${domain}" in plugin "${manifest.name}" already declared by plugin "${owner}". Use plugins.manifests in PluginCatalogConfig to change one plugin's domains or credentials.`
1687
- );
1688
- }
1689
- }
1690
- const definition = {
1691
- manifest,
1692
- dir: pluginDir,
1693
- ...migrationsDir ? { migrationsDir } : void 0,
1694
- ...skillsDir ? { skillsDir } : void 0
1695
- };
1696
- state.pluginDefinitions.push(definition);
1697
- state.pluginsByName.set(manifest.name, definition);
1698
- if (definition.migrationsDir) {
1699
- state.pluginMigrationRoots.set(manifest.name, definition.migrationsDir);
1700
- }
1701
- for (const key of manifest.configKeys) {
1702
- state.pluginConfigKeys.add(key);
1703
- }
1704
- for (const domain of providerDomains(manifest)) {
1705
- state.domainToPlugin.set(domain, manifest.name);
1706
- }
1707
- }
1708
- function registerYamlPluginManifest(state, raw, pluginDir, config) {
1709
- const manifest = parsePluginManifest(raw, pluginDir, config);
1710
- const candidateSkillsDir = path.join(pluginDir, "skills");
1711
- const hasSkillsDir = (() => {
1712
- try {
1713
- return statSync(candidateSkillsDir).isDirectory();
1714
- } catch {
1715
- return false;
1716
- }
1717
- })();
1718
- registerPluginManifest(
1719
- state,
1720
- manifest,
1721
- pluginDir,
1722
- hasSkillsDir ? candidateSkillsDir : void 0
1723
- );
1724
- }
1725
- function normalizePluginRoots(roots) {
1726
- const resolved = [];
1727
- const seen = /* @__PURE__ */ new Set();
1728
- for (const root of roots) {
1729
- const normalized = path.resolve(root);
1730
- if (seen.has(normalized)) {
1731
- continue;
1732
- }
1733
- seen.add(normalized);
1734
- resolved.push(normalized);
1735
- }
1736
- return resolved;
1737
- }
1738
- function getPluginCatalogSource(runtime) {
1739
- const packagedContent = discoverConfiguredPluginPackageContent(
1740
- runtime.pluginConfig
1741
- );
1742
- const localRoots = normalizePluginRoots(pluginRoots());
1743
- const manifestRoots = normalizePluginRoots([
1744
- ...localRoots,
1745
- ...packagedContent.manifestRoots
1746
- ]);
1747
- const packagedSkillRoots = normalizePluginRoots(packagedContent.skillRoots);
1748
- const inlineManifests = runtime.pluginConfig?.inlineManifests ?? [];
1749
- return {
1750
- inlineManifests,
1751
- manifestRoots,
1752
- packagedSkillRoots,
1753
- packagedContent,
1754
- signature: JSON.stringify({
1755
- inlineManifests,
1756
- manifestRoots,
1757
- packages: packagedContent.packages.map((pkg) => ({
1758
- dir: path.resolve(pkg.dir),
1759
- hasMigrationsDir: pkg.hasMigrationsDir,
1760
- hasSkillsDir: pkg.hasSkillsDir,
1761
- packageName: pkg.packageName
1762
- })).sort(
1763
- (left, right) => left.packageName.localeCompare(right.packageName)
1764
- ),
1765
- packagedSkillRoots,
1766
- packageNames: [...packagedContent.packageNames].sort(),
1767
- pluginConfig: runtime.pluginConfig ?? {}
1768
- })
1769
- };
1770
- }
1771
- function normalizePluginCatalogConfig(config) {
1772
- if (!config) {
1773
- return void 0;
1774
- }
1775
- return {
1776
- inlineManifests: config.inlineManifests ? structuredClone(config.inlineManifests) : void 0,
1777
- packages: normalizePluginPackageNames(config.packages),
1778
- ...config.manifests ? { manifests: structuredClone(config.manifests) } : void 0
1779
- };
1780
- }
1781
- function clonePluginCatalogConfig(config) {
1782
- if (!config) {
1783
- return void 0;
1784
- }
1785
- return {
1786
- ...config.inlineManifests ? { inlineManifests: structuredClone(config.inlineManifests) } : void 0,
1787
- packages: [...config.packages ?? []],
1788
- ...config.manifests ? { manifests: structuredClone(config.manifests) } : void 0
1789
- };
1790
- }
1791
- function packageContentByName(packagedContent, packageName) {
1792
- return packagedContent.packages.find(
1793
- (pkg) => pkg.packageName === packageName
1794
- );
1795
- }
1796
- function registerInlineManifests(state, source, config) {
1797
- const migrationOwners = /* @__PURE__ */ new Map();
1798
- for (const definition of source.inlineManifests) {
1799
- const pkg = definition.packageName ? packageContentByName(source.packagedContent, definition.packageName) : void 0;
1800
- const dir = pkg?.dir ?? process.cwd();
1801
- const skillsDir = pkg?.hasSkillsDir ? path.join(pkg.dir, "skills") : void 0;
1802
- const migrationsDir = pkg?.hasMigrationsDir && statSync(path.join(pkg.dir, "migrations"), {
1803
- throwIfNoEntry: false
1804
- })?.isDirectory() ? path.join(pkg.dir, "migrations") : void 0;
1805
- const manifest = parseInlinePluginManifest(
1806
- definition.manifest,
1807
- dir,
1808
- config
1809
- );
1810
- if (migrationsDir) {
1811
- const owner = migrationOwners.get(migrationsDir);
1812
- if (owner) {
1813
- throw new Error(
1814
- `Plugin "${manifest.name}" cannot share migrations directory with plugin "${owner}"`
1815
- );
1816
- }
1817
- migrationOwners.set(migrationsDir, manifest.name);
1818
- }
1819
- registerPluginManifest(state, manifest, dir, skillsDir, migrationsDir);
1820
- }
1821
- }
1822
- function discoverConfiguredPluginPackageContent(config) {
1823
- return discoverInstalledPluginPackageContent(process.cwd(), {
1824
- packageNames: config?.packages
1825
- });
1826
- }
1827
- function buildLoadedPluginState(source, config) {
1828
- const state = createLoadedPluginState(source.signature);
1829
- for (const skillRoot of source.packagedSkillRoots) {
1830
- state.packageSkillRoots.add(skillRoot);
1831
- }
1832
- registerInlineManifests(state, source, config);
1833
- const roots = source.manifestRoots;
1834
- for (const pluginsRoot of roots) {
1835
- let entries;
1836
- let rootStat;
1837
- try {
1838
- rootStat = statSync(pluginsRoot);
1839
- } catch (error) {
1840
- logWarn("plugin.root.read.failed", {
1841
- "file.directory": pluginsRoot,
1842
- "exception.message": error instanceof Error ? error.message : String(error)
1843
- });
1844
- continue;
1845
- }
1846
- if (rootStat.isDirectory()) {
1847
- const manifestPath = path.join(pluginsRoot, "plugin.yaml");
1848
- let hasRootManifest = false;
1849
- try {
1850
- hasRootManifest = statSync(manifestPath).isFile();
1851
- } catch {
1852
- hasRootManifest = false;
1853
- }
1854
- if (hasRootManifest) {
1855
- const rawRootManifest = readFileSync(manifestPath, "utf8");
1856
- registerYamlPluginManifest(state, rawRootManifest, pluginsRoot, config);
1857
- continue;
1858
- }
1859
- }
1860
- try {
1861
- entries = readdirSync(pluginsRoot);
1862
- } catch (error) {
1863
- logWarn("plugin.root.read.failed", {
1864
- "file.directory": pluginsRoot,
1865
- "exception.message": error instanceof Error ? error.message : String(error)
1866
- });
1867
- continue;
1868
- }
1869
- for (const entry of entries.sort()) {
1870
- const pluginDir = path.join(pluginsRoot, entry);
1871
- try {
1872
- const stat = statSync(pluginDir);
1873
- if (!stat.isDirectory()) continue;
1874
- } catch {
1875
- continue;
1876
- }
1877
- const manifestPath = path.join(pluginDir, "plugin.yaml");
1878
- let raw;
1879
- try {
1880
- raw = readFileSync(manifestPath, "utf8");
1881
- } catch {
1882
- continue;
1883
- }
1884
- registerYamlPluginManifest(state, raw, pluginDir, config);
1885
- }
1886
- }
1887
- for (const name of Object.keys(config?.manifests ?? {})) {
1888
- if (!state.pluginsByName.has(name)) {
1889
- throw new Error(
1890
- `plugins.manifests.${name} does not match a loaded plugin`
1891
- );
1892
- }
1893
- }
1894
- return state;
1895
- }
1896
- function logLoadedPlugins(state) {
1897
- const loggedPluginNames = getLoggedPluginNames();
1898
- for (const plugin of [...state.pluginDefinitions].sort(
1899
- (left, right) => left.manifest.name.localeCompare(right.manifest.name)
1900
- )) {
1901
- if (loggedPluginNames.has(plugin.manifest.name)) {
1902
- continue;
1903
- }
1904
- loggedPluginNames.add(plugin.manifest.name);
1905
- logInfo("plugin.loaded", {
1906
- "app.plugin.name": plugin.manifest.name,
1907
- "app.plugin.config_key_count": plugin.manifest.configKeys.length,
1908
- "app.plugin.has_mcp": Boolean(plugin.manifest.mcp),
1909
- "file.directory": plugin.dir,
1910
- ...plugin.skillsDir ? { "app.file.skill_directory": plugin.skillsDir } : void 0
1911
- });
1912
- }
1913
- }
1914
- function ensurePluginsLoaded(runtime) {
1915
- const source = getPluginCatalogSource(runtime);
1916
- if (runtime.loadedPluginState?.signature === source.signature) {
1917
- return runtime.loadedPluginState;
1918
- }
1919
- const state = buildLoadedPluginState(source, runtime.pluginConfig);
1920
- runtime.loadedPluginState = state;
1921
- logLoadedPlugins(state);
1922
- return state;
1923
- }
1924
- function createPluginCatalogRuntime() {
1925
- const runtime = {};
1926
- return {
1927
- setConfig(config) {
1928
- const previousConfig = clonePluginCatalogConfig(runtime.pluginConfig);
1929
- runtime.pluginConfig = normalizePluginCatalogConfig(config);
1930
- return previousConfig;
1931
- },
1932
- parseConfiguredInlineManifest(manifest, dir) {
1933
- return parseInlinePluginManifest(manifest, dir, runtime.pluginConfig);
1934
- },
1935
- getPackageContent() {
1936
- return discoverConfiguredPluginPackageContent(runtime.pluginConfig);
1937
- },
1938
- getSignature() {
1939
- return ensurePluginsLoaded(runtime).signature;
1940
- },
1941
- getProviders() {
1942
- return [...ensurePluginsLoaded(runtime).pluginDefinitions];
1943
- },
1944
- getMigrationRoots() {
1945
- const state = ensurePluginsLoaded(runtime);
1946
- return [...state.pluginMigrationRoots.entries()].map(([pluginName, dir]) => ({ pluginName, dir })).sort((left, right) => left.pluginName.localeCompare(right.pluginName));
1947
- },
1948
- getMcpProviders() {
1949
- return ensurePluginsLoaded(runtime).pluginDefinitions.filter(
1950
- (plugin) => Boolean(plugin.manifest.mcp)
1951
- );
1952
- },
1953
- getRuntimeDependencies() {
1954
- const state = ensurePluginsLoaded(runtime);
1955
- const seen = /* @__PURE__ */ new Set();
1956
- const deps = [];
1957
- for (const plugin of state.pluginDefinitions) {
1958
- for (const dep of plugin.manifest.runtimeDependencies ?? []) {
1959
- const key = dep.type === "npm" ? `${dep.type}:${dep.package}:${dep.version}` : "package" in dep ? `${dep.type}:package:${dep.package}` : `${dep.type}:url:${dep.url}:${dep.sha256}`;
1960
- if (seen.has(key)) {
1961
- continue;
1962
- }
1963
- seen.add(key);
1964
- deps.push(dep);
1965
- }
1966
- }
1967
- return deps.sort((left, right) => {
1968
- if (left.type !== right.type) {
1969
- return left.type.localeCompare(right.type);
1970
- }
1971
- const leftIdentity = "package" in left ? `package:${left.package}` : `url:${left.url}:${left.sha256}`;
1972
- const rightIdentity = "package" in right ? `package:${right.package}` : `url:${right.url}:${right.sha256}`;
1973
- if (leftIdentity !== rightIdentity) {
1974
- return leftIdentity.localeCompare(rightIdentity);
1975
- }
1976
- if (left.type === "npm" && right.type === "npm") {
1977
- return left.version.localeCompare(right.version);
1978
- }
1979
- return 0;
1980
- });
1981
- },
1982
- getRuntimePostinstall() {
1983
- const state = ensurePluginsLoaded(runtime);
1984
- const commands = [];
1985
- for (const plugin of state.pluginDefinitions) {
1986
- for (const command of plugin.manifest.runtimePostinstall ?? []) {
1987
- commands.push({
1988
- cmd: command.cmd,
1989
- ...command.args ? { args: [...command.args] } : void 0,
1990
- ...command.sudo !== void 0 ? { sudo: command.sudo } : void 0
1991
- });
1992
- }
1993
- }
1994
- return commands;
1995
- },
1996
- getOAuthConfig(provider) {
1997
- const plugin = ensurePluginsLoaded(runtime).pluginsByName.get(provider);
1998
- if (!plugin?.manifest.oauth) return void 0;
1999
- const oauth = plugin.manifest.oauth;
2000
- return {
2001
- clientIdEnv: oauth.clientIdEnv,
2002
- clientSecretEnv: oauth.clientSecretEnv,
2003
- authorizeEndpoint: oauth.authorizeEndpoint,
2004
- tokenEndpoint: oauth.tokenEndpoint,
2005
- ...oauth.scope ? { scope: oauth.scope } : void 0,
2006
- ...oauth.authorizeParams ? { authorizeParams: { ...oauth.authorizeParams } } : void 0,
2007
- ...oauth.tokenAuthMethod ? { tokenAuthMethod: oauth.tokenAuthMethod } : void 0,
2008
- ...oauth.tokenExtraHeaders ? { tokenExtraHeaders: { ...oauth.tokenExtraHeaders } } : void 0,
2009
- ...oauth.treatEmptyScopeAsUnreported ? { treatEmptyScopeAsUnreported: true } : void 0,
2010
- callbackPath: `/api/oauth/callback/${plugin.manifest.name}`
2011
- };
2012
- },
2013
- getSkillRoots() {
2014
- const state = ensurePluginsLoaded(runtime);
2015
- return [
2016
- .../* @__PURE__ */ new Set([
2017
- ...state.pluginDefinitions.flatMap(
2018
- (plugin) => plugin.skillsDir ? [plugin.skillsDir] : []
2019
- ),
2020
- ...state.packageSkillRoots
2021
- ])
2022
- ];
2023
- },
2024
- getForSkillPath(skillPath) {
2025
- const state = ensurePluginsLoaded(runtime);
2026
- const resolvedSkillPath = path.resolve(skillPath);
2027
- return state.pluginDefinitions.find((plugin) => {
2028
- if (!plugin.skillsDir) {
2029
- return false;
2030
- }
2031
- const resolvedSkillsDir = path.resolve(plugin.skillsDir);
2032
- return resolvedSkillPath === resolvedSkillsDir || resolvedSkillPath.startsWith(`${resolvedSkillsDir}${path.sep}`);
2033
- });
2034
- },
2035
- getDefinition(provider) {
2036
- return ensurePluginsLoaded(runtime).pluginsByName.get(provider);
2037
- },
2038
- getDisplayName(provider) {
2039
- return ensurePluginsLoaded(runtime).pluginsByName.get(provider)?.manifest.displayName;
2040
- },
2041
- isProvider(provider) {
2042
- return ensurePluginsLoaded(runtime).pluginsByName.has(provider);
2043
- },
2044
- isConfigKey(key) {
2045
- return ensurePluginsLoaded(runtime).pluginConfigKeys.has(key);
2046
- },
2047
- createBroker(provider, deps) {
2048
- const plugin = ensurePluginsLoaded(runtime).pluginsByName.get(provider);
2049
- if (!plugin) {
2050
- throw new Error(`Unknown plugin provider: "${provider}"`);
2051
- }
2052
- const { credentials, name } = plugin.manifest;
2053
- if (!credentials && !plugin.manifest.apiHeaders) {
2054
- throw new Error(
2055
- `Provider "${name}" has no credentials or API headers configured`
2056
- );
2057
- }
2058
- let broker;
2059
- if (!credentials) {
2060
- broker = createApiHeadersBroker(plugin.manifest);
2061
- } else {
2062
- broker = createOAuthBearerBroker(plugin.manifest, credentials, deps);
2063
- }
2064
- setSpanAttributes({
2065
- "app.plugin.name": name,
2066
- "app.plugin.has_oauth": Boolean(plugin.manifest.oauth)
2067
- });
2068
- return broker;
2069
- }
2070
- };
2071
- }
2072
-
2073
- export {
2074
- parsePluginManifest,
2075
- parseInlinePluginManifest,
2076
- CredentialUnavailableError,
2077
- hasRequiredOAuthScope,
2078
- OAuthProviderError,
2079
- getOAuthProviderErrorAttributes,
2080
- fetchWithBoundedOAuthErrorBodies,
2081
- resolvePluginCommandEnv,
2082
- resolveAuthTokenPlaceholder,
2083
- buildOAuthTokenRequest,
2084
- parseOAuthTokenResponse,
2085
- createPluginCatalogRuntime
2086
- };