@apifuse/provider-sdk 2.2.0-beta.2 → 2.2.0-beta.21

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 (291) hide show
  1. package/AUTHORING.md +487 -0
  2. package/CHANGELOG.md +90 -0
  3. package/README.md +49 -3
  4. package/SUBMISSION.md +1 -1
  5. package/bin/apifuse-check.ts +44 -59
  6. package/bin/apifuse-create.ts +1 -1
  7. package/bin/apifuse-dev.ts +27 -52
  8. package/bin/apifuse-pack-check.ts +14 -0
  9. package/bin/apifuse-pack-smoke.ts +36 -81
  10. package/bin/apifuse-pack-types.ts +305 -0
  11. package/bin/apifuse-perf.ts +45 -127
  12. package/bin/apifuse-record.ts +659 -111
  13. package/bin/apifuse-submit-check.ts +546 -48
  14. package/bin/apifuse-sync-assets.ts +117 -0
  15. package/bin/apifuse.ts +1 -1
  16. package/bin/submit-check-delimited-text.ts +50 -0
  17. package/bin/submit-check-xml-semantics.ts +204 -0
  18. package/bin/submit-check-xml.ts +134 -0
  19. package/dist/auth-turn/index.d.ts +3 -3
  20. package/dist/auth.d.ts +2 -2
  21. package/dist/auth.js +9 -18
  22. package/dist/ceremonies/index.d.ts +9 -1
  23. package/dist/ceremonies/index.js +65 -18
  24. package/dist/cli/commands.d.ts +1 -1
  25. package/dist/cli/commands.js +8 -0
  26. package/dist/cli/create.d.ts +3 -0
  27. package/dist/cli/create.js +34 -35
  28. package/dist/cli/prompt-assets.d.ts +80 -0
  29. package/dist/cli/prompt-assets.js +743 -0
  30. package/dist/cli/templates/provider/AGENTS.md.tpl +17 -8
  31. package/dist/config/loader.d.ts +176 -8
  32. package/dist/config/loader.js +424 -95
  33. package/dist/contract-serialization.d.ts +2 -2
  34. package/dist/contract-serialization.js +3 -6
  35. package/dist/contract-types.d.ts +2 -2
  36. package/dist/contract.d.ts +3 -3
  37. package/dist/contract.js +4 -6
  38. package/dist/define.d.ts +9 -1
  39. package/dist/define.js +304 -119
  40. package/dist/dev.d.ts +1 -1
  41. package/dist/dev.js +1 -1
  42. package/dist/error-resolution.d.ts +2 -0
  43. package/dist/error-resolution.js +90 -0
  44. package/dist/errors.d.ts +22 -1
  45. package/dist/errors.js +88 -0
  46. package/dist/fixture-sanitization.d.ts +26 -0
  47. package/dist/fixture-sanitization.js +216 -0
  48. package/dist/i18n/catalog.d.ts +2 -2
  49. package/dist/i18n/catalog.js +4 -10
  50. package/dist/i18n/index.d.ts +2 -2
  51. package/dist/i18n/index.js +2 -2
  52. package/dist/i18n/keys.d.ts +2 -2
  53. package/dist/index.d.ts +46 -42
  54. package/dist/index.js +41 -37
  55. package/dist/lint.d.ts +1 -1
  56. package/dist/lint.js +8 -15
  57. package/dist/native-address.d.ts +43 -0
  58. package/dist/native-address.js +281 -0
  59. package/dist/native-egress-policy.d.ts +31 -0
  60. package/dist/native-egress-policy.js +288 -0
  61. package/dist/observability.d.ts +5 -2
  62. package/dist/observability.js +48 -1
  63. package/dist/provider.d.ts +12 -11
  64. package/dist/provider.js +10 -9
  65. package/dist/public-schema-field-lint.d.ts +1 -1
  66. package/dist/recipes/gov-api.js +1 -1
  67. package/dist/runtime/auth-flow.d.ts +2 -1
  68. package/dist/runtime/auth-flow.js +4 -3
  69. package/dist/runtime/browser.d.ts +1 -1
  70. package/dist/runtime/browser.js +15 -29
  71. package/dist/runtime/cache.d.ts +1 -1
  72. package/dist/runtime/cache.js +4 -8
  73. package/dist/runtime/choice.d.ts +1 -1
  74. package/dist/runtime/choice.js +31 -35
  75. package/dist/runtime/credential.d.ts +1 -1
  76. package/dist/runtime/credential.js +1 -1
  77. package/dist/runtime/env.d.ts +1 -1
  78. package/dist/runtime/executor.d.ts +1 -1
  79. package/dist/runtime/executor.js +31 -4
  80. package/dist/runtime/http.d.ts +2 -2
  81. package/dist/runtime/http.js +387 -47
  82. package/dist/runtime/insights.d.ts +1 -1
  83. package/dist/runtime/insights.js +6 -13
  84. package/dist/runtime/instrumentation.d.ts +2 -2
  85. package/dist/runtime/instrumentation.js +345 -22
  86. package/dist/runtime/keyring.js +1 -1
  87. package/dist/runtime/namespace.js +1 -1
  88. package/dist/runtime/native-network.d.ts +127 -0
  89. package/dist/runtime/native-network.js +1298 -0
  90. package/dist/runtime/otlp.d.ts +1 -1
  91. package/dist/runtime/perf.d.ts +1 -1
  92. package/dist/runtime/provider.d.ts +1 -1
  93. package/dist/runtime/provider.js +1 -2
  94. package/dist/runtime/proxy-errors.d.ts +1 -1
  95. package/dist/runtime/proxy-errors.js +9 -7
  96. package/dist/runtime/proxy-nodemaven.d.ts +56 -0
  97. package/dist/runtime/proxy-nodemaven.js +146 -0
  98. package/dist/runtime/proxy-retry-policy.d.ts +2 -2
  99. package/dist/runtime/proxy-retry-policy.js +2 -2
  100. package/dist/runtime/proxy-telemetry.d.ts +2 -1
  101. package/dist/runtime/proxy-telemetry.js +55 -52
  102. package/dist/runtime/redirects.d.ts +29 -0
  103. package/dist/runtime/redirects.js +36 -0
  104. package/dist/runtime/redis.d.ts +1 -1
  105. package/dist/runtime/redis.js +2 -4
  106. package/dist/runtime/request-options.d.ts +68 -1
  107. package/dist/runtime/request-options.js +548 -0
  108. package/dist/runtime/secrets.d.ts +27 -0
  109. package/dist/runtime/secrets.js +51 -0
  110. package/dist/runtime/state.d.ts +2 -2
  111. package/dist/runtime/state.js +238 -26
  112. package/dist/runtime/stealth.d.ts +5 -3
  113. package/dist/runtime/stealth.js +423 -143
  114. package/dist/runtime/stt.d.ts +1 -1
  115. package/dist/runtime/stt.js +11 -15
  116. package/dist/runtime/trace.d.ts +2 -2
  117. package/dist/runtime/trace.js +2 -4
  118. package/dist/runtime/waterfall.d.ts +1 -1
  119. package/dist/schema.d.ts +1 -1
  120. package/dist/schema.js +7 -15
  121. package/dist/serve.d.ts +1 -1
  122. package/dist/serve.js +1 -1
  123. package/dist/server/index.d.ts +7 -7
  124. package/dist/server/index.js +6 -6
  125. package/dist/server/self-test-input-tokens.d.ts +2 -1
  126. package/dist/server/self-test-input-tokens.js +18 -14
  127. package/dist/server/self-test-redaction.d.ts +1 -1
  128. package/dist/server/self-test-redaction.js +1 -1
  129. package/dist/server/self-test.d.ts +104 -3
  130. package/dist/server/self-test.js +673 -115
  131. package/dist/server/serve.d.ts +116 -4
  132. package/dist/server/serve.js +799 -128
  133. package/dist/server/types.d.ts +34 -9
  134. package/dist/server/types.js +8 -1
  135. package/dist/stateful/errors.d.ts +14 -0
  136. package/dist/stateful/errors.js +14 -0
  137. package/dist/stateful/http-provider-event-emitter.d.ts +40 -0
  138. package/dist/stateful/http-provider-event-emitter.js +237 -0
  139. package/dist/stateful/http-session-owner-registry.d.ts +44 -0
  140. package/dist/stateful/http-session-owner-registry.js +210 -0
  141. package/dist/stateful/index.d.ts +18 -0
  142. package/dist/stateful/index.js +18 -0
  143. package/dist/stateful/provider-event-delivery-failures.d.ts +32 -0
  144. package/dist/stateful/provider-event-delivery-failures.js +43 -0
  145. package/dist/stateful/provider-event-pipeline-metrics.d.ts +46 -0
  146. package/dist/stateful/provider-event-pipeline-metrics.js +48 -0
  147. package/dist/stateful/provider-event-pipeline.d.ts +50 -0
  148. package/dist/stateful/provider-event-pipeline.js +1 -0
  149. package/dist/stateful/provider-events.d.ts +101 -0
  150. package/dist/stateful/provider-events.js +289 -0
  151. package/dist/stateful/session-key.d.ts +15 -0
  152. package/dist/stateful/session-key.js +86 -0
  153. package/dist/stateful/stateful-provider-adapter-context.d.ts +5 -0
  154. package/dist/stateful/stateful-provider-adapter-context.js +42 -0
  155. package/dist/stateful/stateful-provider-adapter-metrics.d.ts +15 -0
  156. package/dist/stateful/stateful-provider-adapter-metrics.js +21 -0
  157. package/dist/stateful/stateful-provider-adapter.d.ts +98 -0
  158. package/dist/stateful/stateful-provider-adapter.js +287 -0
  159. package/dist/stateful/stateful-provider-observability.d.ts +62 -0
  160. package/dist/stateful/stateful-provider-observability.js +161 -0
  161. package/dist/stateful/stateful-provider-owner-forwarder.d.ts +41 -0
  162. package/dist/stateful/stateful-provider-owner-forwarder.js +215 -0
  163. package/dist/stateful/stateful-provider-runtime-context.d.ts +32 -0
  164. package/dist/stateful/stateful-provider-runtime-context.js +60 -0
  165. package/dist/stateful/stateful-provider-runtime-executor.d.ts +34 -0
  166. package/dist/stateful/stateful-provider-runtime-executor.js +52 -0
  167. package/dist/stateful/stateful-provider-session-routing.d.ts +71 -0
  168. package/dist/stateful/stateful-provider-session-routing.js +353 -0
  169. package/dist/stateful/stateful-provider-session-runtime.d.ts +98 -0
  170. package/dist/stateful/stateful-provider-session-runtime.js +245 -0
  171. package/dist/stateful-signing.d.ts +18 -0
  172. package/dist/stateful-signing.js +27 -0
  173. package/dist/stealth/profiles.d.ts +1 -1
  174. package/dist/stealth/profiles.js +5 -14
  175. package/dist/stream-evidence.d.ts +74 -0
  176. package/dist/stream-evidence.js +785 -0
  177. package/dist/stream.d.ts +1 -1
  178. package/dist/testing/index.d.ts +2 -2
  179. package/dist/testing/index.js +2 -2
  180. package/dist/testing/run.d.ts +32 -2
  181. package/dist/testing/run.js +478 -28
  182. package/dist/types.d.ts +342 -14
  183. package/dist/types.js +1 -0
  184. package/dist/user-input.d.ts +30 -0
  185. package/dist/user-input.js +66 -0
  186. package/package.json +16 -5
  187. package/src/auth-turn/index.ts +1 -1
  188. package/src/auth.ts +28 -86
  189. package/src/ceremonies/index.ts +103 -78
  190. package/src/cli/commands.ts +10 -0
  191. package/src/cli/create.ts +42 -35
  192. package/src/cli/prompt-assets.ts +865 -0
  193. package/src/cli/templates/provider/AGENTS.md.tpl +17 -8
  194. package/src/config/loader.ts +652 -204
  195. package/src/contract-serialization.ts +5 -11
  196. package/src/contract-types.ts +2 -2
  197. package/src/contract.ts +12 -28
  198. package/src/define.ts +499 -498
  199. package/src/dev.ts +4 -9
  200. package/src/error-resolution.ts +91 -0
  201. package/src/errors.ts +110 -5
  202. package/src/fixture-sanitization.ts +247 -0
  203. package/src/i18n/catalog.ts +10 -32
  204. package/src/i18n/index.ts +2 -2
  205. package/src/i18n/keys.ts +5 -11
  206. package/src/index.ts +112 -42
  207. package/src/lint.ts +88 -152
  208. package/src/native-address.ts +340 -0
  209. package/src/native-egress-policy.ts +358 -0
  210. package/src/observability.ts +51 -1
  211. package/src/provider.ts +66 -11
  212. package/src/public-schema-field-lint.ts +7 -33
  213. package/src/recipes/gov-api.ts +2 -5
  214. package/src/runtime/auth-flow.ts +7 -7
  215. package/src/runtime/browser.ts +64 -187
  216. package/src/runtime/cache.ts +20 -67
  217. package/src/runtime/choice.ts +79 -132
  218. package/src/runtime/credential.ts +2 -2
  219. package/src/runtime/env.ts +1 -1
  220. package/src/runtime/executor.ts +43 -20
  221. package/src/runtime/http.ts +494 -57
  222. package/src/runtime/insights.ts +15 -53
  223. package/src/runtime/instrumentation.ts +495 -66
  224. package/src/runtime/keyring.ts +7 -19
  225. package/src/runtime/namespace.ts +2 -7
  226. package/src/runtime/native-network.ts +1686 -0
  227. package/src/runtime/otlp.ts +12 -23
  228. package/src/runtime/perf.ts +1 -1
  229. package/src/runtime/provider.ts +4 -9
  230. package/src/runtime/proxy-errors.ts +29 -42
  231. package/src/runtime/proxy-nodemaven.ts +221 -0
  232. package/src/runtime/proxy-retry-policy.ts +3 -3
  233. package/src/runtime/proxy-telemetry.ts +79 -77
  234. package/src/runtime/redirects.ts +66 -0
  235. package/src/runtime/redis.ts +4 -12
  236. package/src/runtime/request-options.ts +679 -9
  237. package/src/runtime/secrets.ts +64 -0
  238. package/src/runtime/state.ts +353 -133
  239. package/src/runtime/stealth.ts +505 -154
  240. package/src/runtime/stt.ts +38 -94
  241. package/src/runtime/trace.ts +14 -44
  242. package/src/runtime/waterfall.ts +5 -18
  243. package/src/schema.ts +23 -84
  244. package/src/serve.ts +1 -1
  245. package/src/server/index.ts +29 -7
  246. package/src/server/self-test-input-tokens.ts +29 -14
  247. package/src/server/self-test-redaction.ts +2 -2
  248. package/src/server/self-test.ts +857 -132
  249. package/src/server/serve.ts +1151 -328
  250. package/src/server/types.ts +12 -13
  251. package/src/stateful/README.md +146 -0
  252. package/src/stateful/errors.ts +23 -0
  253. package/src/stateful/http-provider-event-emitter.ts +314 -0
  254. package/src/stateful/http-session-owner-registry.ts +306 -0
  255. package/src/stateful/index.ts +18 -0
  256. package/src/stateful/provider-event-delivery-failures.ts +80 -0
  257. package/src/stateful/provider-event-pipeline-metrics.ts +95 -0
  258. package/src/stateful/provider-event-pipeline.ts +61 -0
  259. package/src/stateful/provider-events.ts +462 -0
  260. package/src/stateful/session-key.ts +111 -0
  261. package/src/stateful/stateful-provider-adapter-context.ts +59 -0
  262. package/src/stateful/stateful-provider-adapter-metrics.ts +48 -0
  263. package/src/stateful/stateful-provider-adapter.ts +562 -0
  264. package/src/stateful/stateful-provider-observability.ts +261 -0
  265. package/src/stateful/stateful-provider-owner-forwarder.ts +287 -0
  266. package/src/stateful/stateful-provider-runtime-context.ts +92 -0
  267. package/src/stateful/stateful-provider-runtime-executor.ts +96 -0
  268. package/src/stateful/stateful-provider-session-routing.ts +555 -0
  269. package/src/stateful/stateful-provider-session-runtime.ts +403 -0
  270. package/src/stateful-signing.ts +46 -0
  271. package/src/stealth/profiles.ts +10 -26
  272. package/src/stream-evidence.ts +988 -0
  273. package/src/stream.ts +8 -19
  274. package/src/testing/index.ts +10 -2
  275. package/src/testing/run.ts +653 -74
  276. package/src/types.ts +408 -28
  277. package/src/user-input.ts +118 -0
  278. package/dist/cli/templates/provider/CLAUDE.md.tpl +0 -1
  279. package/src/cli/templates/provider/CLAUDE.md.tpl +0 -1
  280. /package/dist/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
  281. /package/dist/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
  282. /package/dist/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
  283. /package/dist/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
  284. /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
  285. /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
  286. /package/src/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
  287. /package/src/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
  288. /package/src/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
  289. /package/src/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
  290. /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
  291. /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
package/README.md CHANGED
@@ -119,7 +119,11 @@ the bad request path; provider/runtime failures include `code`, `message`, and
119
119
  local-only values through `connection.secrets`. Read them in handlers with
120
120
  `ctx.credential.get("key")` or `ctx.credential.getAccessToken()`.
121
121
  - **Provider env secrets**: declare `secrets[]`, set values in your shell or
122
- `.env`, and read only those names through `ctx.env.get("NAME")`.
122
+ `.env`, and read only those names through `ctx.env.get("NAME")`. The SDK
123
+ enforces presence of `required: true` declarations before handlers and auth
124
+ flows run, failing the invocation with a structured `MISSING_SECRET` error
125
+ (HTTP 400, category `credential_unavailable`) — do not re-check presence in
126
+ handlers.
123
127
  - **Credentials auth flows**: prefer `defineCredentialsAuth()` over hand-written
124
128
  `auth.flow`. Declare the form fields and credential keys once, then put the
125
129
  upstream login/session creation in `login(ctx, input)`. Return
@@ -134,14 +138,56 @@ the bad request path; provider/runtime failures include `code`, `message`, and
134
138
  - **Stealth-sensitive providers**: use `ctx.http` for normal JSON/REST calls and
135
139
  `ctx.stealth.fetch()` when you need browser-like session or cookie control.
136
140
  `ctx.stealth.fetch()` uses the impit-backed browser stealth transport and
137
- accepts request controls for `params`, `proxy`, `timeout`, `profile`,
138
- `redirect`, `throwOnHttpError`, and `stealth.insecureSkipVerify`. For login
141
+ accepts request controls for `params`, `sensitiveParams`, `proxy`, `timeout`, `profile`,
142
+ `maxBodyBytes`, `redirect`, `throwOnHttpError`, and
143
+ `stealth.insecureSkipVerify`. For login
139
144
  flows that must inspect intermediate `Location`/`Set-Cookie` headers, create
140
145
  a session with `ctx.stealth.createSession()` and use `session.redirects.run()`;
141
146
  inspect accumulated cookies through `session.cookies`. Select an SDK stealth
142
147
  `profile` such as `chrome-146`; do not tune JA3, HTTP/2 SETTINGS, or
143
148
  pseudo-header order in provider code. Chrome/Firefox-style profiles are
144
149
  supported; use `ctx.browser` when Safari-specific behavior is required.
150
+ - **Query-parameter credentials**: when an upstream requires a credential in
151
+ its URL query, pass it through `sensitiveParams`, not `params` and never a
152
+ hand-built URL. It is sent as a normal query parameter while the SDK redacts
153
+ its value from transport errors, traces, and recorded fixtures:
154
+
155
+ ```ts
156
+ const response = await ctx.http.get("/openapi/service", {
157
+ params: { page: 1 },
158
+ sensitiveParams: { serviceKey: ctx.env.get("APIFUSE__PROVIDER__EXAMPLE__API_KEY")! },
159
+ });
160
+ ```
161
+
162
+ Use this only when the upstream offers no header or body credential channel.
163
+ Declared query-key positions and exact scalar diagnostics are always redacted.
164
+ Free-text values of four or more characters are redacted as substrings; shorter
165
+ values require token boundaries so low-entropy values do not corrupt unrelated
166
+ words or timestamps. Consequently, a sub-four-character secret embedded in a
167
+ larger alphanumeric token can remain in free text; prefer higher-entropy or
168
+ non-query credentials when possible. `sensitiveParams: {}` is equivalent to
169
+ omitting the option. Redirect results structurally redact declared keys and
170
+ common response-only credential keys from hop URLs; use
171
+ `redirects.run({ stopWhen })` to inspect a real intermediate `Location` during
172
+ the run when a login flow needs a rotated value.
173
+ - **Proxy URLs for non-stealth consumers**: use `resolveProxy()` when a
174
+ provider-owned client outside `ctx.stealth` needs the provider's proxy, such
175
+ as a CAPTCHA solver that must use matching egress. Pass the provider proxy
176
+ policy used by the provider and consume the returned `url`; the SDK owns
177
+ vendor selection, allocation, failover, and URL formats. Never call proxy
178
+ allocator APIs or hardcode proxy vendor hostnames in provider code.
179
+
180
+ ```ts
181
+ import { resolveProxy } from "@apifuse/provider-sdk"
182
+
183
+ const resolvedProxy = await resolveProxy({
184
+ proxyPolicy,
185
+ affinityKey: connectionId,
186
+ })
187
+
188
+ if (!resolvedProxy.url) throw new Error("This login requires proxy egress")
189
+ const captchaTask = { proxy: resolvedProxy.url }
190
+ ```
145
191
  - **Browser providers**: for TypeScript Providers use `runtime: "browser"` plus
146
192
  `browser.engine: "playwright-stealth"`; `nodriver` is a Python-runtime path.
147
193
  Install local browser assets with `bunx playwright install chromium` when
package/SUBMISSION.md CHANGED
@@ -50,7 +50,7 @@ Fix all blockers before submitting:
50
50
  - High-confidence secret or token material in source, README, package metadata, or fixtures.
51
51
  - SDK-native source blockers: prefixed Provider ids, `vendor/` SDK shims or imports, raw `.describe()` prose instead of `describeKey`, raw global `fetch()` calls, and excessive `as Type` assertions.
52
52
 
53
- Warnings do not fail the command, but they should be addressed when practical. For example, the generated starter `ping` operation warns because it is not a real upstream-backed bounty Operation. SDK-native warnings also flag moderate `as Type` assertion counts and credentialed Providers that never reference `ctx.credential`.
53
+ Warnings do not fail the command, but they should be addressed when practical. For example, the generated starter `ping` operation warns because it is not a real upstream-backed bounty Operation. SDK-native warnings also flag moderate `as Type` assertion counts, credentialed Providers that never reference `ctx.credential`, and provider-local re-validation of declared env secrets (`sdk-owned-secret-presence`, 0 points): the SDK already rejects invocations with a structured `MISSING_SECRET` error when a declared `required: true` secret is unset, so `requireServiceKey`-style presence guards are deprecated dead weight — delete the guard and read the value directly with `ctx.env.get()`, or acknowledge a deliberate exception with `// @apifuse-allow sdk-owned-secret-presence: <reason>`.
54
54
 
55
55
  ## Measured local smoke
56
56
 
@@ -6,9 +6,14 @@ import { pathToFileURL } from "node:url";
6
6
 
7
7
  import { z } from "zod";
8
8
 
9
- import type { ProviderDefinition } from "../src";
10
- import { lintProvider, type ProviderLintMode } from "../src/lint";
11
- import { safeParseSchemaSync } from "../src/schema";
9
+ import {
10
+ formatPromptAssetIssues,
11
+ PROMPT_ASSET_SYNC_REMEDIATION,
12
+ verifyPromptAssets,
13
+ } from "../src/cli/prompt-assets.js";
14
+ import type { ProviderDefinition } from "../src/index.js";
15
+ import { lintProvider, type ProviderLintMode } from "../src/lint.js";
16
+ import { safeParseSchemaSync } from "../src/schema.js";
12
17
 
13
18
  const HELP_TEXT = `Usage: apifuse check [path]
14
19
  Example: apifuse check providers/korea-air-quality
@@ -24,9 +29,7 @@ export type RunChecksOptions = {
24
29
  lintMode?: ProviderLintMode;
25
30
  };
26
31
 
27
- type SafeParseResult =
28
- | { success: true; data: unknown }
29
- | { success: false; error: unknown };
32
+ type SafeParseResult = { success: true; data: unknown } | { success: false; error: unknown };
30
33
 
31
34
  export async function main() {
32
35
  const args = normalizeArgs(process.argv.slice(2));
@@ -63,7 +66,7 @@ function normalizeArgs(argv: string[]): string[] {
63
66
  return argv[0] === "check" ? argv.slice(1) : argv;
64
67
  }
65
68
 
66
- function resolveProviderRoot(inputPath: string): string {
69
+ export function resolveProviderRoot(inputPath: string): string {
67
70
  const resolvedInput = resolveFromParents(inputPath);
68
71
 
69
72
  if (!existsSync(resolvedInput)) {
@@ -131,9 +134,26 @@ export async function runChecks(
131
134
  checkProviderMetadata(provider),
132
135
  checkDockerfile(dockerfilePath),
133
136
  checkPackageJson(packageJsonPath),
137
+ checkPromptAssets(providerRoot),
134
138
  ];
135
139
  }
136
140
 
141
+ export const PROMPT_ASSETS_CHECK_MESSAGE =
142
+ "Agent prompt assets match the installed SDK version";
143
+
144
+ function checkPromptAssets(providerRoot: string): CheckResult {
145
+ const verification = verifyPromptAssets(providerRoot);
146
+ if (verification.ok) {
147
+ return { message: PROMPT_ASSETS_CHECK_MESSAGE, passed: true };
148
+ }
149
+
150
+ return {
151
+ message: PROMPT_ASSETS_CHECK_MESSAGE,
152
+ passed: false,
153
+ details: [...formatPromptAssetIssues(verification), PROMPT_ASSET_SYNC_REMEDIATION],
154
+ };
155
+ }
156
+
137
157
  function isScannableProviderSourceFile(relativePath: string): boolean {
138
158
  return (
139
159
  /\.(?:ts|tsx|js|jsx|mjs|cjs|sh|bash)$/.test(relativePath) ||
@@ -142,17 +162,12 @@ function isScannableProviderSourceFile(relativePath: string): boolean {
142
162
  );
143
163
  }
144
164
 
145
- function collectProviderSourceFiles(
146
- providerRoot: string,
147
- ): Record<string, string> {
165
+ function collectProviderSourceFiles(providerRoot: string): Record<string, string> {
148
166
  const sources: Record<string, string> = {};
149
- const skipDirectories = new Set([
150
- ".git",
151
- "node_modules",
152
- "dist",
153
- "build",
154
- ".next",
155
- ]);
167
+ // `.agents`/`.apifuse` are deliberately not skipped: managed content there
168
+ // is markdown/JSON (never matched by isScannableProviderSourceFile), and a
169
+ // planted `.ts`/`.sh` under those directories must stay in scanner scope.
170
+ const skipDirectories = new Set([".git", "node_modules", "dist", "build", ".next"]);
156
171
  const visit = (directory: string) => {
157
172
  for (const entry of readdirSync(directory, { withFileTypes: true })) {
158
173
  const path = resolve(directory, entry.name);
@@ -162,10 +177,7 @@ function collectProviderSourceFiles(
162
177
  }
163
178
  continue;
164
179
  }
165
- if (
166
- !entry.isFile() ||
167
- !isScannableProviderSourceFile(path.slice(providerRoot.length + 1))
168
- ) {
180
+ if (!entry.isFile() || !isScannableProviderSourceFile(path.slice(providerRoot.length + 1))) {
169
181
  continue;
170
182
  }
171
183
  sources[path.slice(providerRoot.length + 1)] = readFileSync(path, "utf8");
@@ -175,10 +187,7 @@ function collectProviderSourceFiles(
175
187
  return sources;
176
188
  }
177
189
 
178
- function checkIndex(
179
- indexPath: string,
180
- provider: ProviderDefinition | undefined,
181
- ): CheckResult {
190
+ function checkIndex(indexPath: string, provider: ProviderDefinition | undefined): CheckResult {
182
191
  if (!existsSync(indexPath)) {
183
192
  return {
184
193
  message: "index.ts exists and exports default defineProvider",
@@ -200,9 +209,7 @@ function checkIndex(
200
209
  };
201
210
  }
202
211
 
203
- function checkOperations(
204
- provider: ProviderDefinition | undefined,
205
- ): CheckResult {
212
+ function checkOperations(provider: ProviderDefinition | undefined): CheckResult {
206
213
  if (!provider) {
207
214
  return {
208
215
  message: "All operations have handler, input, output",
@@ -277,20 +284,14 @@ function checkSchemas(provider: ProviderDefinition | undefined): CheckResult {
277
284
  continue;
278
285
  }
279
286
 
280
- const requestResult = parseFixture(
281
- operation.input,
282
- operation.fixtures.request,
283
- );
287
+ const requestResult = parseFixture(operation.input, operation.fixtures.request);
284
288
  if (!requestResult.success) {
285
289
  failures.push(
286
290
  `${operationId}: request fixture invalid (${formatSchemaError(requestResult.error)})`,
287
291
  );
288
292
  }
289
293
 
290
- const responseResult = parseFixture(
291
- operation.output,
292
- operation.fixtures.response,
293
- );
294
+ const responseResult = parseFixture(operation.output, operation.fixtures.response);
294
295
  if (!responseResult.success) {
295
296
  failures.push(
296
297
  `${operationId}: response fixture invalid (${formatSchemaError(responseResult.error)})`,
@@ -317,13 +318,8 @@ function checkAuthoringLint(
317
318
  };
318
319
  }
319
320
 
320
- const diagnostics = lintProvider(
321
- { ...provider, providerSourceFiles },
322
- { mode: lintMode },
323
- );
324
- const errors = diagnostics.filter(
325
- (diagnostic) => diagnostic.level === "error",
326
- );
321
+ const diagnostics = lintProvider({ ...provider, providerSourceFiles }, { mode: lintMode });
322
+ const errors = diagnostics.filter((diagnostic) => diagnostic.level === "error");
327
323
  const details = diagnostics.map((diagnostic) => {
328
324
  const field = diagnostic.field ? `${diagnostic.field}: ` : "";
329
325
  return `${diagnostic.level.toUpperCase()} ${diagnostic.rule} ${field}${diagnostic.message}`;
@@ -336,9 +332,7 @@ function checkAuthoringLint(
336
332
  };
337
333
  }
338
334
 
339
- function checkProviderMetadata(
340
- provider: ProviderDefinition | undefined,
341
- ): CheckResult {
335
+ function checkProviderMetadata(provider: ProviderDefinition | undefined): CheckResult {
342
336
  if (!provider) {
343
337
  return {
344
338
  message: "Provider metadata is declared in defineProvider",
@@ -425,18 +419,12 @@ function checkPackageJson(packageJsonPath: string): CheckResult {
425
419
  }
426
420
  }
427
421
 
428
- function assertProviderDefinition(
429
- value: unknown,
430
- ): ProviderDefinition | undefined {
422
+ function assertProviderDefinition(value: unknown): ProviderDefinition | undefined {
431
423
  return isProviderDefinition(value) ? value : undefined;
432
424
  }
433
425
 
434
426
  function isProviderDefinition(value: unknown): value is ProviderDefinition {
435
- if (
436
- !isRecord(value) ||
437
- !isRecord(value.meta) ||
438
- !isRecord(value.operations)
439
- ) {
427
+ if (!isRecord(value) || !isRecord(value.meta) || !isRecord(value.operations)) {
440
428
  return false;
441
429
  }
442
430
 
@@ -457,8 +445,7 @@ function hasSchemaParser(value: unknown): boolean {
457
445
  return (
458
446
  isRecord(value) &&
459
447
  (typeof value.safeParse === "function" ||
460
- (isRecord(value["~standard"]) &&
461
- typeof value["~standard"].validate === "function"))
448
+ (isRecord(value["~standard"]) && typeof value["~standard"].validate === "function"))
462
449
  );
463
450
  }
464
451
 
@@ -470,9 +457,7 @@ function formatSchemaError(error: unknown): string {
470
457
  if (Array.isArray(error)) {
471
458
  return error
472
459
  .map((issue) =>
473
- isRecord(issue) && typeof issue.message === "string"
474
- ? issue.message
475
- : String(issue),
460
+ isRecord(issue) && typeof issue.message === "string" ? issue.message : String(issue),
476
461
  )
477
462
  .join(", ");
478
463
  }
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
- import { main as runMain } from "../src/cli/create";
3
+ import { main as runMain } from "../src/cli/create.js";
4
4
 
5
5
  export { runMain as main };
6
6
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { existsSync } from "node:fs";
4
4
  import { dirname, relative, resolve } from "node:path";
5
- import type { ProviderDefinition } from "../src";
5
+ import type { ProviderDefinition } from "../src/index.js";
6
6
  import {
7
7
  createBrowserClient,
8
8
  createCredentialContext,
@@ -14,10 +14,10 @@ import {
14
14
  createSttClientFromEnv,
15
15
  PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV,
16
16
  ProviderError,
17
- } from "../src";
18
- import { createMemoryProviderRuntimeState } from "../src/runtime/state";
19
- import { createTraceContext } from "../src/runtime/trace";
20
- import type { BrowserClient, ProviderContext } from "../src/types";
17
+ } from "../src/index.js";
18
+ import { createMemoryProviderRuntimeState } from "../src/runtime/state.js";
19
+ import { createTraceContext } from "../src/runtime/trace.js";
20
+ import type { BrowserClient, ProviderContext } from "../src/types.js";
21
21
 
22
22
  const HELP_TEXT = `Usage: apifuse dev [path]
23
23
  Example: apifuse dev providers/korea-air-quality
@@ -33,12 +33,9 @@ export async function main() {
33
33
 
34
34
  const providerPath = resolveProviderPath(args[0] ?? ".");
35
35
  const providerModule = await import(resolve(providerPath, "index.ts"));
36
- const provider = assertProviderDefinition(
37
- providerModule.default,
38
- providerPath,
39
- );
36
+ const provider = assertProviderDefinition(providerModule.default, providerPath);
40
37
 
41
- const { startDevServer } = await import("../src/dev");
38
+ const { startDevServer } = await import("../src/dev.js");
42
39
  const port = Number(process.env.APIFUSE__RUNTIME__PORT) || 3900;
43
40
 
44
41
  startDevServer(provider, { port });
@@ -57,8 +54,7 @@ export async function main() {
57
54
 
58
55
  const firstOperation = Object.keys(provider.operations)[0];
59
56
  if (firstOperation) {
60
- const sampleInput =
61
- provider.operations[firstOperation]?.fixtures?.request ?? {};
57
+ const sampleInput = provider.operations[firstOperation]?.fixtures?.request ?? {};
62
58
  const sampleBody = JSON.stringify({
63
59
  requestId: `req_local_${firstOperation}`,
64
60
  input: sampleInput,
@@ -149,9 +145,7 @@ function renderHotReloadCommand(providerPath: string, port: number): string {
149
145
  const devEntry = resolve(providerPath, "dev.ts");
150
146
  if (existsSync(devEntry)) {
151
147
  const relativeDevEntry = relative(process.cwd(), devEntry) || "dev.ts";
152
- const portPrefix = process.env.APIFUSE__RUNTIME__PORT
153
- ? `APIFUSE__RUNTIME__PORT=${port} `
154
- : "";
148
+ const portPrefix = process.env.APIFUSE__RUNTIME__PORT ? `APIFUSE__RUNTIME__PORT=${port} ` : "";
155
149
  return `${portPrefix}bun --hot ${relativeDevEntry}`;
156
150
  }
157
151
  return "rerun `apifuse dev` after edits (no dev.ts entrypoint found)";
@@ -166,40 +160,28 @@ function createUnsupportedBrowserStub(): BrowserClient {
166
160
  engine: "playwright-stealth",
167
161
  async close() {},
168
162
  async newPage() {
169
- throw new ProviderError(
170
- "Browser runtime is not enabled for this provider",
171
- {
172
- code: "BROWSER_RUNTIME_UNSUPPORTED",
173
- fix: 'Set provider runtime to "browser" to use ctx.browser',
174
- },
175
- );
163
+ throw new ProviderError("Browser runtime is not enabled for this provider", {
164
+ code: "BROWSER_RUNTIME_UNSUPPORTED",
165
+ fix: 'Set provider runtime to "browser" to use ctx.browser',
166
+ });
176
167
  },
177
168
  async rawPage() {
178
- throw new ProviderError(
179
- "Browser runtime is not enabled for this provider",
180
- {
181
- code: "BROWSER_RUNTIME_UNSUPPORTED",
182
- fix: 'Set provider runtime to "browser" and APIFUSE__CDP_POOL__URL to use ctx.browser.rawPage',
183
- },
184
- );
169
+ throw new ProviderError("Browser runtime is not enabled for this provider", {
170
+ code: "BROWSER_RUNTIME_UNSUPPORTED",
171
+ fix: 'Set provider runtime to "browser" and APIFUSE__CDP_POOL__URL to use ctx.browser.rawPage',
172
+ });
185
173
  },
186
174
  async withIsolatedContext() {
187
- throw new ProviderError(
188
- "Browser runtime is not enabled for this provider",
189
- {
190
- code: "BROWSER_RUNTIME_UNSUPPORTED",
191
- fix: 'Set provider runtime to "browser" to use ctx.browser.withIsolatedContext',
192
- },
193
- );
175
+ throw new ProviderError("Browser runtime is not enabled for this provider", {
176
+ code: "BROWSER_RUNTIME_UNSUPPORTED",
177
+ fix: 'Set provider runtime to "browser" to use ctx.browser.withIsolatedContext',
178
+ });
194
179
  },
195
180
  async solveChallenge() {
196
- throw new ProviderError(
197
- "Browser runtime is not enabled for this provider",
198
- {
199
- code: "BROWSER_RUNTIME_UNSUPPORTED",
200
- fix: 'Set provider runtime to "browser" to use ctx.browser.solveChallenge',
201
- },
202
- );
181
+ throw new ProviderError("Browser runtime is not enabled for this provider", {
182
+ code: "BROWSER_RUNTIME_UNSUPPORTED",
183
+ fix: 'Set provider runtime to "browser" to use ctx.browser.solveChallenge',
184
+ });
203
185
  },
204
186
  };
205
187
  }
@@ -216,10 +198,7 @@ function createUnsupportedAuthStub() {
216
198
  };
217
199
  }
218
200
 
219
- function assertProviderDefinition(
220
- value: unknown,
221
- providerPath: string,
222
- ): ProviderDefinition {
201
+ function assertProviderDefinition(value: unknown, providerPath: string): ProviderDefinition {
223
202
  if (!isProviderDefinition(value)) {
224
203
  throw new Error(
225
204
  `Expected ${resolve(providerPath, "index.ts")} to export default defineProvider(...)`,
@@ -230,11 +209,7 @@ function assertProviderDefinition(
230
209
  }
231
210
 
232
211
  function isProviderDefinition(value: unknown): value is ProviderDefinition {
233
- if (
234
- !isRecord(value) ||
235
- !isRecord(value.meta) ||
236
- !isRecord(value.operations)
237
- ) {
212
+ if (!isRecord(value) || !isRecord(value.meta) || !isRecord(value.operations)) {
238
213
  return false;
239
214
  }
240
215
 
@@ -48,6 +48,13 @@ const requiredPaths = [
48
48
  "src/cli/templates/provider/operations/ping.ts.tpl",
49
49
  "src/cli/templates/provider/schemas/ping.ts.tpl",
50
50
  "src/cli/templates/provider/upstream/README.md.tpl",
51
+ "src/cli/templates/provider/AGENTS.md.tpl",
52
+ "src/cli/templates/provider/.agents/skills/normalization-standards/SKILL.md.tpl",
53
+ "src/cli/templates/provider/.agents/skills/upstream-contract-verification/SKILL.md.tpl",
54
+ "src/cli/templates/provider/.agents/skills/fixtures-and-recording/SKILL.md.tpl",
55
+ "src/cli/templates/provider/.agents/skills/pagination-and-counts/SKILL.md.tpl",
56
+ "src/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl",
57
+ "src/cli/templates/provider/.agents/skills/upstream-notes/README.md.tpl",
51
58
  "dist/cli/templates/provider/.dockerignore.tpl",
52
59
  "dist/cli/templates/provider/.gitignore.tpl",
53
60
  "dist/cli/templates/provider/Dockerfile.tpl",
@@ -60,6 +67,13 @@ const requiredPaths = [
60
67
  "dist/cli/templates/provider/operations/ping.ts.tpl",
61
68
  "dist/cli/templates/provider/schemas/ping.ts.tpl",
62
69
  "dist/cli/templates/provider/upstream/README.md.tpl",
70
+ "dist/cli/templates/provider/AGENTS.md.tpl",
71
+ "dist/cli/templates/provider/.agents/skills/normalization-standards/SKILL.md.tpl",
72
+ "dist/cli/templates/provider/.agents/skills/upstream-contract-verification/SKILL.md.tpl",
73
+ "dist/cli/templates/provider/.agents/skills/fixtures-and-recording/SKILL.md.tpl",
74
+ "dist/cli/templates/provider/.agents/skills/pagination-and-counts/SKILL.md.tpl",
75
+ "dist/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl",
76
+ "dist/cli/templates/provider/.agents/skills/upstream-notes/README.md.tpl",
63
77
  "dist/auth-turn/index.js",
64
78
  "dist/auth-turn/index.d.ts",
65
79
  "dist/auth-turn/auth-turn.v1.schema.json",
@@ -1,19 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
- import {
4
- type ChildProcess,
5
- execFileSync,
6
- spawn,
7
- spawnSync,
8
- } from "node:child_process";
9
- import {
10
- existsSync,
11
- mkdirSync,
12
- mkdtempSync,
13
- readFileSync,
14
- rmSync,
15
- writeFileSync,
16
- } from "node:fs";
3
+ import { type ChildProcess, execFileSync, spawn, spawnSync } from "node:child_process";
4
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
17
5
  import { createServer } from "node:net";
18
6
  import { tmpdir } from "node:os";
19
7
  import { join, resolve } from "node:path";
@@ -41,9 +29,7 @@ const PING_RESPONSE_SCHEMA = z.object({
41
29
 
42
30
  const KEEP_TEMP = process.env.APIFUSE__PACK_SMOKE__KEEP_TEMP === "1";
43
31
 
44
- const tempRoot = mkdtempSync(
45
- join(tmpdir(), "apifuse-provider-sdk-pack-smoke-"),
46
- );
32
+ const tempRoot = mkdtempSync(join(tmpdir(), "apifuse-provider-sdk-pack-smoke-"));
47
33
  const packDir = join(tempRoot, "pack");
48
34
  const consumerDir = join(tempRoot, "consumer");
49
35
  const externalWorkspaceDir = join(tempRoot, "external-workspace");
@@ -73,6 +59,20 @@ try {
73
59
  );
74
60
 
75
61
  run("bun", ["install"], consumerDir);
62
+ run(
63
+ "bun",
64
+ [
65
+ "--eval",
66
+ [
67
+ 'import { resolveProxy } from "@apifuse/provider-sdk";',
68
+ 'if (typeof resolveProxy !== "function") throw new Error("resolveProxy is not exported");',
69
+ 'const resolved = await resolveProxy({ proxy: "http://127.0.0.1:8080" });',
70
+ 'if (resolved.url !== "http://127.0.0.1:8080") throw new Error("resolveProxy returned the wrong URL");',
71
+ 'console.log("packed root resolveProxy export OK");',
72
+ ].join("\n"),
73
+ ],
74
+ consumerDir,
75
+ );
76
76
 
77
77
  const cliBin = join(consumerDir, "node_modules", ".bin", "apifuse");
78
78
  if (!existsSync(cliBin)) {
@@ -81,15 +81,7 @@ try {
81
81
 
82
82
  run(
83
83
  "bun",
84
- [
85
- cliBin,
86
- "create",
87
- "dx-smoke",
88
- "--yes",
89
- "--json",
90
- "--sdk-specifier",
91
- tarballSpecifier,
92
- ],
84
+ [cliBin, "create", "dx-smoke", "--yes", "--json", "--sdk-specifier", tarballSpecifier],
93
85
  consumerDir,
94
86
  );
95
87
 
@@ -99,11 +91,7 @@ try {
99
91
  run("bun", ["run", "test"], generatedProviderDir);
100
92
  assertGeneratedReadme(generatedProviderDir);
101
93
  await smokeGeneratedDevServer(generatedProviderDir);
102
- assertExternalWorkspaceTopology(
103
- cliBin,
104
- externalWorkspaceDir,
105
- tarballSpecifier,
106
- );
94
+ assertExternalWorkspaceTopology(cliBin, externalWorkspaceDir, tarballSpecifier);
107
95
 
108
96
  console.log(
109
97
  `Provider SDK packed-artifact smoke passed: ${tarballPath} -> ${generatedProviderDir}`,
@@ -148,15 +136,8 @@ function assertExternalWorkspaceTopology(
148
136
  externalWorkspaceDir,
149
137
  );
150
138
 
151
- const generatedProviderDir = join(
152
- externalWorkspaceDir,
153
- "external-workspace-smoke",
154
- );
155
- const forbiddenProviderDir = join(
156
- externalWorkspaceDir,
157
- "providers",
158
- "external-workspace-smoke",
159
- );
139
+ const generatedProviderDir = join(externalWorkspaceDir, "external-workspace-smoke");
140
+ const forbiddenProviderDir = join(externalWorkspaceDir, "providers", "external-workspace-smoke");
160
141
  if (!existsSync(generatedProviderDir)) {
161
142
  throw new Error(
162
143
  "Public create must generate a one-provider repository at <name>/ even when providers/ exists.",
@@ -168,9 +149,7 @@ function assertExternalWorkspaceTopology(
168
149
  );
169
150
  }
170
151
 
171
- const packageJson = JSON.parse(
172
- readFileSync(join(generatedProviderDir, "package.json"), "utf8"),
173
- );
152
+ const packageJson = JSON.parse(readFileSync(join(generatedProviderDir, "package.json"), "utf8"));
174
153
  const sdkDependency = packageJson?.dependencies?.["@apifuse/provider-sdk"];
175
154
  if (sdkDependency !== tarballSpecifier) {
176
155
  throw new Error(
@@ -178,9 +157,7 @@ function assertExternalWorkspaceTopology(
178
157
  );
179
158
  }
180
159
  if (JSON.stringify(packageJson).includes("workspace:")) {
181
- throw new Error(
182
- "External bounty workspace scaffold must not contain workspace: dependencies.",
183
- );
160
+ throw new Error("External bounty workspace scaffold must not contain workspace: dependencies.");
184
161
  }
185
162
 
186
163
  run("bun", ["install"], generatedProviderDir);
@@ -199,30 +176,20 @@ function assertExternalWorkspaceTopology(
199
176
  },
200
177
  );
201
178
  if (monorepoAttempt.status === 0) {
202
- throw new Error(
203
- "--preset monorepo must reject outside the private APIFuse monorepo.",
204
- );
179
+ throw new Error("--preset monorepo must reject outside the private APIFuse monorepo.");
205
180
  }
206
181
  const rejectionOutput = `${monorepoAttempt.stdout}\n${monorepoAttempt.stderr}`;
207
- if (
208
- !rejectionOutput.includes(
209
- "Monorepo preset is internal to the APIFuse repository",
210
- )
211
- ) {
182
+ if (!rejectionOutput.includes("Monorepo preset is internal to the APIFuse repository")) {
212
183
  throw new Error(`Unexpected monorepo rejection output: ${rejectionOutput}`);
213
184
  }
214
185
  }
215
186
 
216
187
  function packSdk(destination: string): { filename: string } {
217
- const raw = execFileSync(
218
- "npm",
219
- ["pack", "--json", "--pack-destination", destination],
220
- {
221
- cwd: process.cwd(),
222
- encoding: "utf8",
223
- stdio: ["ignore", "pipe", "inherit"],
224
- },
225
- );
188
+ const raw = execFileSync("npm", ["pack", "--json", "--pack-destination", destination], {
189
+ cwd: process.cwd(),
190
+ encoding: "utf8",
191
+ stdio: ["ignore", "pipe", "inherit"],
192
+ });
226
193
  const parsed = PACK_RESULT_SCHEMA.parse(JSON.parse(raw));
227
194
  const first = parsed[0];
228
195
  if (!first) {
@@ -252,29 +219,19 @@ function run(command: string, args: string[], cwd: string): void {
252
219
  function assertGeneratedReadme(providerDir: string): void {
253
220
  const readme = readFileSync(join(providerDir, "README.md"), "utf8");
254
221
  if (!readme.includes('"requestId":"req_local_ping"')) {
255
- throw new Error(
256
- "Generated README is missing requestId in local smoke docs.",
257
- );
222
+ throw new Error("Generated README is missing requestId in local smoke docs.");
258
223
  }
259
224
  if (readme.includes('"connection":null')) {
260
- throw new Error(
261
- "Generated README must not document connection:null for no-auth local smoke.",
262
- );
225
+ throw new Error("Generated README must not document connection:null for no-auth local smoke.");
263
226
  }
264
227
  if (!readme.includes("bunx playwright install chromium")) {
265
- throw new Error(
266
- "Generated README is missing browser runtime troubleshooting guidance.",
267
- );
228
+ throw new Error("Generated README is missing browser runtime troubleshooting guidance.");
268
229
  }
269
230
  if (!readme.includes("impit")) {
270
- throw new Error(
271
- "Generated README is missing impit stealth runtime guidance.",
272
- );
231
+ throw new Error("Generated README is missing impit stealth runtime guidance.");
273
232
  }
274
233
  if (!readme.includes("bun run submit-check")) {
275
- throw new Error(
276
- "Generated README must document the submit-check pre-submission workflow.",
277
- );
234
+ throw new Error("Generated README must document the submit-check pre-submission workflow.");
278
235
  }
279
236
  if (!readme.includes("bun run record -- --operation <operation>")) {
280
237
  throw new Error(
@@ -368,9 +325,7 @@ async function waitForHttp(
368
325
 
369
326
  while (Date.now() < deadline) {
370
327
  if (server.exitCode !== null) {
371
- throw new Error(
372
- `Dev server exited early with code ${server.exitCode}\n${getOutput()}`,
373
- );
328
+ throw new Error(`Dev server exited early with code ${server.exitCode}\n${getOutput()}`);
374
329
  }
375
330
 
376
331
  try {