@kubb/agent 5.0.0-alpha.48 → 5.0.0-alpha.49

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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "date": "2026-04-19T08:11:44.537Z",
2
+ "date": "2026-04-19T17:00:21.900Z",
3
3
  "preset": "node-server",
4
4
  "framework": {
5
5
  "name": "nitro",
@@ -6,7 +6,7 @@ import http, { Server as Server$1 } from 'node:http';
6
6
  import https, { Server } from 'node:https';
7
7
  import { EventEmitter } from 'node:events';
8
8
  import { Buffer as Buffer$1 } from 'node:buffer';
9
- import { promises, existsSync } from 'node:fs';
9
+ import { promises, existsSync, readdirSync } from 'node:fs';
10
10
  import path$1, { resolve, dirname, relative, join, extname, posix } from 'node:path';
11
11
  import anymatch from 'anymatch';
12
12
  import { createHash, randomBytes } from 'node:crypto';
@@ -4501,13 +4501,23 @@ async function isFormatterAvailable(formatter) {
4501
4501
  }
4502
4502
  async function detectFormatter() {
4503
4503
  const formatterNames = /* @__PURE__ */ new Set([
4504
- "biome",
4505
4504
  "oxfmt",
4505
+ "biome",
4506
4506
  "prettier"
4507
4507
  ]);
4508
4508
  for (const formatter of formatterNames) if (await isFormatterAvailable(formatter)) return formatter;
4509
4509
  return null;
4510
4510
  }
4511
+ function findLintableFiles(dir) {
4512
+ try {
4513
+ return readdirSync(dir, {
4514
+ withFileTypes: true,
4515
+ recursive: true
4516
+ }).filter((d) => d.isFile()).map((d) => `${d.parentPath}/${d.name}`);
4517
+ } catch {
4518
+ return [];
4519
+ }
4520
+ }
4511
4521
  const linters = {
4512
4522
  eslint: {
4513
4523
  command: "eslint",
@@ -4525,7 +4535,7 @@ const linters = {
4525
4535
  },
4526
4536
  oxlint: {
4527
4537
  command: "oxlint",
4528
- args: (outputPath) => ["--fix", outputPath],
4538
+ args: (outputPath) => ["--fix", ...findLintableFiles(outputPath)],
4529
4539
  errorMessage: "Oxlint not found"
4530
4540
  }
4531
4541
  };
@@ -4538,8 +4548,8 @@ async function isLinterAvailable(linter) {
4538
4548
  }
4539
4549
  async function detectLinter() {
4540
4550
  const linterNames = /* @__PURE__ */ new Set([
4541
- "biome",
4542
4551
  "oxlint",
4552
+ "biome",
4543
4553
  "eslint"
4544
4554
  ]);
4545
4555
  for (const linter of linterNames) if (await isLinterAvailable(linter)) return linter;
@@ -4612,7 +4622,9 @@ async function createAgentSession({ token, studioUrl }) {
4612
4622
  logger.info("Created agent session with Studio");
4613
4623
  return data;
4614
4624
  } catch (error) {
4615
- throw new Error("Failed to get agent session from Kubb Studio", { cause: error });
4625
+ throw new Error("Failed to get agent session from Kubb Studio", {
4626
+ cause: error
4627
+ });
4616
4628
  }
4617
4629
  }
4618
4630
  async function registerAgent({ token, studioUrl, poolSize }) {
@@ -4645,7 +4657,9 @@ async function disconnect({ sessionId, token, studioUrl }) {
4645
4657
  });
4646
4658
  logger.success(`[${maskedSessionKey}] Disconnected from Studio`);
4647
4659
  } catch (error) {
4648
- throw new Error("Failed to notify Studio of disconnection on exit", { cause: error });
4660
+ throw new Error("Failed to notify Studio of disconnection on exit", {
4661
+ cause: error
4662
+ });
4649
4663
  }
4650
4664
  }
4651
4665
 
@@ -6277,7 +6291,7 @@ const fsStorage = createStorage(() => ({
6277
6291
  await clean(resolve(base));
6278
6292
  }
6279
6293
  }));
6280
- var version$1 = "5.0.0-alpha.48";
6294
+ var version$1 = "5.0.0-alpha.49";
6281
6295
  function getDiagnosticInfo() {
6282
6296
  return {
6283
6297
  nodeVersion: version$2,
@@ -6957,7 +6971,7 @@ const memoryStorage = createStorage(() => {
6957
6971
  };
6958
6972
  });
6959
6973
 
6960
- var version = "5.0.0-alpha.48";
6974
+ var version = "5.0.0-alpha.49";
6961
6975
 
6962
6976
  function isCommandMessage(msg) {
6963
6977
  return msg.type === "command";
@@ -7036,7 +7050,7 @@ async function generate({ config, hooks }) {
7036
7050
  if (formatter === "auto") {
7037
7051
  const detectedFormatter = await detectFormatter();
7038
7052
  if (!detectedFormatter) {
7039
- await hooks.emit("kubb:warn", "No formatter found (biome, prettier, or oxfmt). Skipping formatting.");
7053
+ await hooks.emit("kubb:warn", "No formatter found (oxfmt, biome, or prettier). Skipping formatting.");
7040
7054
  } else {
7041
7055
  formatter = detectedFormatter;
7042
7056
  await hooks.emit("kubb:info", `Auto-detected formatter: ${formatter}`);
@@ -7070,7 +7084,7 @@ async function generate({ config, hooks }) {
7070
7084
  if (linter === "auto") {
7071
7085
  const detectedLinter = await detectLinter();
7072
7086
  if (!detectedLinter) {
7073
- await hooks.emit("kubb:warn", "No linter found (biome, oxlint, or eslint). Skipping linting.");
7087
+ await hooks.emit("kubb:warn", "No linter found (oxlint, biome, or eslint). Skipping linting.");
7074
7088
  } else {
7075
7089
  linter = detectedLinter;
7076
7090
  await hooks.emit("kubb:info", `Auto-detected linter: ${styleText("dim", linter)}`);
@@ -7253,10 +7267,22 @@ function setupHookListener(hooks, root) {
7253
7267
  throwOnError: true
7254
7268
  });
7255
7269
  console.log(result.stdout.trimEnd());
7256
- await hooks.emit("kubb:hook:end", { command, args, id, success: true, error: null });
7270
+ await hooks.emit("kubb:hook:end", {
7271
+ command,
7272
+ args,
7273
+ id,
7274
+ success: true,
7275
+ error: null
7276
+ });
7257
7277
  } catch (_err) {
7258
7278
  const errorMessage = new Error(`Hook execute failed: ${commandWithArgs}`);
7259
- await hooks.emit("kubb:hook:end", { command, args, id, success: false, error: errorMessage });
7279
+ await hooks.emit("kubb:hook:end", {
7280
+ command,
7281
+ args,
7282
+ id,
7283
+ success: false,
7284
+ error: errorMessage
7285
+ });
7260
7286
  await hooks.emit("kubb:error", errorMessage);
7261
7287
  }
7262
7288
  });
@@ -7430,7 +7456,9 @@ async function connectToStudio(options) {
7430
7456
  try {
7431
7457
  setupHookListener(hooks, root);
7432
7458
  const { sessionId, wsUrl, isSandbox } = initialSession != null ? initialSession : await createAgentSession({ token, studioUrl });
7433
- const ws = createWebsocket(wsUrl, { headers: { Authorization: `Bearer ${token}` } });
7459
+ const ws = createWebsocket(wsUrl, {
7460
+ headers: { Authorization: `Bearer ${token}` }
7461
+ });
7434
7462
  const maskedWsUrl = maskString(wsUrl);
7435
7463
  const maskedSessionId = maskString(sessionId);
7436
7464
  const effectiveAllowAll = isSandbox ? false : allowAll;
@@ -7516,7 +7544,10 @@ async function connectToStudio(options) {
7516
7544
  logger.warn(`[${maskedSessionId}] Input override via payload is only supported in sandbox mode and will be ignored`);
7517
7545
  }
7518
7546
  if (data.payload && effectiveWrite) {
7519
- await saveStudioConfigToStorage({ sessionId, config: data.payload }).catch((err) => {
7547
+ await saveStudioConfigToStorage({
7548
+ sessionId,
7549
+ config: data.payload
7550
+ }).catch((err) => {
7520
7551
  logger.warn(`[${maskedSessionId}] Failed to save studio config: ${err == null ? void 0 : err.message}`);
7521
7552
  });
7522
7553
  }
@@ -7525,7 +7556,10 @@ async function connectToStudio(options) {
7525
7556
  ...config,
7526
7557
  root,
7527
7558
  input: inputOverride != null ? inputOverride : config.input,
7528
- output: { ...config.output, storage: effectiveWrite ? fsStorage() : memoryStorage() },
7559
+ output: {
7560
+ ...config.output,
7561
+ storage: effectiveWrite ? fsStorage() : memoryStorage()
7562
+ },
7529
7563
  plugins
7530
7564
  },
7531
7565
  hooks
@@ -7541,7 +7575,11 @@ async function connectToStudio(options) {
7541
7575
  payload: {
7542
7576
  version,
7543
7577
  configPath,
7544
- permissions: { allowAll: effectiveAllowAll, allowWrite: effectiveWrite, allowPublish: effectivePublish },
7578
+ permissions: {
7579
+ allowAll: effectiveAllowAll,
7580
+ allowWrite: effectiveWrite,
7581
+ allowPublish: effectivePublish
7582
+ },
7545
7583
  config: {
7546
7584
  plugins: config.plugins.map((plugin) => ({
7547
7585
  name: `@kubb/${plugin.name}`,
@@ -7578,7 +7616,9 @@ async function connectToStudio(options) {
7578
7616
  }
7579
7617
  });
7580
7618
  } catch (error) {
7581
- throw new Error(`[unhandledRejection] ${(_a = error == null ? void 0 : error.message) != null ? _a : error}`, { cause: error });
7619
+ throw new Error(`[unhandledRejection] ${(_a = error == null ? void 0 : error.message) != null ? _a : error}`, {
7620
+ cause: error
7621
+ });
7582
7622
  }
7583
7623
  }
7584
7624
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/agent-prod",
3
- "version": "5.0.0-alpha.48",
3
+ "version": "5.0.0-alpha.49",
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "dependencies": {
package/README.md CHANGED
@@ -88,7 +88,7 @@ services:
88
88
  - agent_kv:/kubb/agent/.kubb/data
89
89
  restart: unless-stopped
90
90
  healthcheck:
91
- test: ["CMD", "node", "-e", "fetch('http://localhost:3000/api/health').then(r => r.ok ? process.exit(0) : process.exit(1)).catch(() => process.exit(1))"]
91
+ test: ['CMD', 'node', '-e', "fetch('http://localhost:3000/api/health').then(r => r.ok ? process.exit(0) : process.exit(1)).catch(() => process.exit(1))"]
92
92
  interval: 15s
93
93
  timeout: 10s
94
94
  start_period: 60s
@@ -106,18 +106,18 @@ The `agent_kv` named volume persists the KV store (session cache, machine token)
106
106
 
107
107
  ### Environment Variables
108
108
 
109
- | Variable | Default | Description |
110
- |------------------------------|---|---|
111
- | `KUBB_AGENT_CONFIG` | `kubb.config.ts` | Path to your Kubb config file. Relative paths are resolved against `KUBB_AGENT_ROOT`. |
112
- | `KUBB_AGENT_ROOT` | `/kubb/agent` (Docker) / `cwd` | Root directory for resolving relative paths. |
113
- | `PORT` | `3000` | Server port. |
114
- | `HOST` | `0.0.0.0` | Server host. |
115
- | `KUBB_STUDIO_URL` | `https://studio.kubb.dev` | Kubb Studio WebSocket URL. |
116
- | `KUBB_AGENT_TOKEN` | _(empty)_ | Authentication token for Studio. Required to connect. |
117
- | `KUBB_AGENT_ALLOW_WRITE` | `false` | Set to `true` to allow writing generated files to disk. |
118
- | `KUBB_AGENT_ALLOW_ALL` | `false` | Set to `true` to grant all permissions (implies `KUBB_AGENT_ALLOW_WRITE=true`). |
119
- | `KUBB_AGENT_RETRY_TIMEOUT` | `30000` | Milliseconds to wait before retrying a failed Studio connection. |
120
- | `KUBB_AGENT_HEARTBEAT_URL` | _(empty)_ | URL to call every 5 minutes to signal the agent is alive (e.g. a Healthchecks.io ping URL). Leave empty to disable. |
109
+ | Variable | Default | Description |
110
+ | -------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------- |
111
+ | `KUBB_AGENT_CONFIG` | `kubb.config.ts` | Path to your Kubb config file. Relative paths are resolved against `KUBB_AGENT_ROOT`. |
112
+ | `KUBB_AGENT_ROOT` | `/kubb/agent` (Docker) / `cwd` | Root directory for resolving relative paths. |
113
+ | `PORT` | `3000` | Server port. |
114
+ | `HOST` | `0.0.0.0` | Server host. |
115
+ | `KUBB_STUDIO_URL` | `https://studio.kubb.dev` | Kubb Studio WebSocket URL. |
116
+ | `KUBB_AGENT_TOKEN` | _(empty)_ | Authentication token for Studio. Required to connect. |
117
+ | `KUBB_AGENT_ALLOW_WRITE` | `false` | Set to `true` to allow writing generated files to disk. |
118
+ | `KUBB_AGENT_ALLOW_ALL` | `false` | Set to `true` to grant all permissions (implies `KUBB_AGENT_ALLOW_WRITE=true`). |
119
+ | `KUBB_AGENT_RETRY_TIMEOUT` | `30000` | Milliseconds to wait before retrying a failed Studio connection. |
120
+ | `KUBB_AGENT_HEARTBEAT_URL` | _(empty)_ | URL to call every 5 minutes to signal the agent is alive (e.g. a Healthchecks.io ping URL). Leave empty to disable. |
121
121
 
122
122
  ### Automatic .env Loading
123
123
 
@@ -126,6 +126,7 @@ The agent automatically loads a `.env` file from the current working directory i
126
126
  ## Quick Start
127
127
 
128
128
  1. **Create `.env` file:**
129
+
129
130
  ```env
130
131
  PORT=3000
131
132
  KUBB_AGENT_ROOT=/path/to/your/project
@@ -135,11 +136,13 @@ KUBB_STUDIO_URL=https://studio.kubb.dev
135
136
  ```
136
137
 
137
138
  2. **Run the agent:**
139
+
138
140
  ```bash
139
141
  npx kubb agent start
140
142
  ```
141
143
 
142
144
  3. **Agent is now available at:**
145
+
143
146
  ```
144
147
  http://localhost:3000
145
148
  ```
@@ -167,6 +170,7 @@ On startup the agent performs these steps before opening a WebSocket:
167
170
  ### Session Caching
168
171
 
169
172
  Sessions are cached in `./.kubb/data` (relative to the working directory, or `agent_kv` volume in Docker) for faster reconnects:
173
+
170
174
  - Tokens are hashed (non-reversible) for security
171
175
  - Sessions auto-expire after 24 hours
172
176
  - Use `--no-cache` flag to disable caching
@@ -177,6 +181,7 @@ Sessions are cached in `./.kubb/data` (relative to the working directory, or `ag
177
181
  ### Messages Sent by Agent
178
182
 
179
183
  **Connected** — sent in response to a `connect` command
184
+
180
185
  ```json
181
186
  {
182
187
  "type": "connected",
@@ -188,15 +193,14 @@ Sessions are cached in `./.kubb/data` (relative to the working directory, or `ag
188
193
  "allowWrite": false
189
194
  },
190
195
  "config": {
191
- "plugins": [
192
- { "name": "@kubb/plugin-ts", "options": {} }
193
- ]
196
+ "plugins": [{ "name": "@kubb/plugin-ts", "options": {} }]
194
197
  }
195
198
  }
196
199
  }
197
200
  ```
198
201
 
199
202
  **Data Events** — streamed during code generation
203
+
200
204
  ```json
201
205
  {
202
206
  "type": "data",
@@ -211,6 +215,7 @@ Sessions are cached in `./.kubb/data` (relative to the working directory, or `ag
211
215
  Available `payload.type` values: `plugin:start`, `plugin:end`, `files:processing:start`, `file:processing:update`, `files:processing:end`, `generation:start`, `generation:end`, `info`, `success`, `warn`, `error`.
212
216
 
213
217
  **Ping** — sent every 30 seconds to keep the connection alive
218
+
214
219
  ```json
215
220
  { "type": "ping" }
216
221
  ```
@@ -218,6 +223,7 @@ Available `payload.type` values: `plugin:start`, `plugin:end`, `files:processing
218
223
  ### Messages Received from Studio
219
224
 
220
225
  **Generate Command** — triggers code generation
226
+
221
227
  ```json
222
228
  {
223
229
  "type": "command",
@@ -225,11 +231,13 @@ Available `payload.type` values: `plugin:start`, `plugin:end`, `files:processing
225
231
  "payload": { "plugins": [] }
226
232
  }
227
233
  ```
234
+
228
235
  `payload` is optional. When omitted, the agent falls back to `kubb.config.studio.json` (a temporal config file next to `kubb.config.ts`), and then to the config loaded from disk.
229
236
 
230
237
  The `payload` may also include an `input` field containing a raw OpenAPI / Swagger spec (YAML or JSON string). **This field is only honoured in sandbox mode** — outside of sandbox it is silently ignored for security reasons. See [Sandbox Mode](#sandbox-mode) below.
231
238
 
232
239
  **Connect Command** — requests agent info
240
+
233
241
  ```json
234
242
  {
235
243
  "type": "command",
@@ -242,19 +250,19 @@ The `payload` may also include an `input` field containing a raw OpenAPI / Swagg
242
250
  ```
243
251
 
244
252
  **Pong** — sent by Studio in response to an agent ping
253
+
245
254
  ```json
246
255
  { "type": "pong" }
247
256
  ```
248
257
 
249
258
  **Status** — sent by Studio with information about connected agents
259
+
250
260
  ```json
251
261
  {
252
262
  "type": "status",
253
263
  "message": "...",
254
264
  "connectedAgents": 1,
255
- "agents": [
256
- { "name": "...", "connectedAt": "..." }
257
- ]
265
+ "agents": [{ "name": "...", "connectedAt": "..." }]
258
266
  }
259
267
  ```
260
268
 
@@ -262,11 +270,11 @@ The `payload` may also include an `input` field containing a raw OpenAPI / Swagg
262
270
 
263
271
  When Kubb Studio provisions a session for the **Sandbox Agent** (the shared agent hosted by Studio itself), it sets `isSandbox: true` in the session response. In sandbox mode the agent behaves differently from a user-owned agent:
264
272
 
265
- | behavior | Normal agent | Sandbox agent |
266
- |---|---|---|
267
- | Write generated files to disk | ✅ (when `KUBB_AGENT_ALLOW_WRITE=true`) | ❌ Never |
268
- | Read `input.path` from disk | ✅ | ✅ (falls back when no inline input supplied) |
269
- | Accept inline `input` in generate payload | ❌ Ignored | ✅ |
273
+ | behavior | Normal agent | Sandbox agent |
274
+ | ----------------------------------------- | --------------------------------------- | --------------------------------------------- |
275
+ | Write generated files to disk | ✅ (when `KUBB_AGENT_ALLOW_WRITE=true`) | ❌ Never |
276
+ | Read `input.path` from disk | ✅ | ✅ (falls back when no inline input supplied) |
277
+ | Accept inline `input` in generate payload | ❌ Ignored | ✅ |
270
278
 
271
279
  ### Why no filesystem writes?
272
280
 
@@ -282,14 +290,12 @@ Because the sandbox agent cannot read arbitrary files from disk, callers must su
282
290
  "command": "generate",
283
291
  "payload": {
284
292
  "input": "openapi: 3.0.0\ninfo:\n title: Pet Store\n version: 1.0.0\n...",
285
- "plugins": [
286
- { "name": "@kubb/plugin-ts", "options": {} }
287
- ]
293
+ "plugins": [{ "name": "@kubb/plugin-ts", "options": {} }]
288
294
  }
289
295
  }
290
296
  ```
291
297
 
292
- The `input` value is treated as `InputData` (i.e. `{ data: "<content>" }`) and overrides the `input` from the loaded config for that generation cycle. **Outside of sandbox mode this field is ignored.**
298
+ The `input` value is treated as `InputData` (i.e. `{ data: "<content>" }`) and overrides the `input` from the loaded config for that generation cycle. **Outside of sandbox mode this field is ignored.**
293
299
 
294
300
  ## Configuration Example
295
301
 
@@ -307,10 +313,7 @@ export default defineConfig({
307
313
  output: {
308
314
  path: './src/generated',
309
315
  },
310
- plugins: [
311
- pluginOas(),
312
- pluginTs(),
313
- ],
316
+ plugins: [pluginOas(), pluginTs()],
314
317
  })
315
318
  ```
316
319
 
package/package.json CHANGED
@@ -1,36 +1,40 @@
1
1
  {
2
2
  "name": "@kubb/agent",
3
- "version": "5.0.0-alpha.48",
3
+ "version": "5.0.0-alpha.49",
4
4
  "description": "Agent server for Kubb, enabling HTTP-based access to code generation capabilities.",
5
5
  "keywords": [
6
6
  "agent",
7
- "server",
7
+ "code-generation",
8
+ "codegen",
8
9
  "http",
9
10
  "kubb",
10
- "code-generation",
11
11
  "openapi",
12
+ "server",
12
13
  "swagger",
13
- "typescript",
14
- "codegen"
14
+ "typescript"
15
15
  ],
16
+ "license": "AGPL-3.0-or-later",
17
+ "author": "stijnvanhulle",
16
18
  "repository": {
17
19
  "type": "git",
18
20
  "url": "git+https://github.com/kubb-labs/kubb.git",
19
21
  "directory": "packages/agent"
20
22
  },
21
- "license": "AGPL-3.0-or-later",
22
- "author": "stijnvanhulle",
23
- "sideEffects": false,
24
- "type": "module",
25
- "exports": {
26
- "./package.json": "./package.json"
27
- },
28
23
  "files": [
29
24
  ".output",
30
25
  "!/**/**.test.**",
31
26
  "!/**/__tests__/**",
32
27
  "!/**/__snapshots__/**"
33
28
  ],
29
+ "type": "module",
30
+ "sideEffects": false,
31
+ "exports": {
32
+ "./package.json": "./package.json"
33
+ },
34
+ "publishConfig": {
35
+ "access": "public",
36
+ "registry": "https://registry.npmjs.org/"
37
+ },
34
38
  "dependencies": {
35
39
  "@logtail/node": "^0.5.8",
36
40
  "consola": "^3.4.2",
@@ -39,8 +43,8 @@
39
43
  "tinyexec": "^1.1.1",
40
44
  "unstorage": "^1.17.5",
41
45
  "ws": "^8.20.0",
42
- "@kubb/ast": "5.0.0-alpha.48",
43
- "@kubb/core": "5.0.0-alpha.48"
46
+ "@kubb/core": "5.0.0-alpha.49",
47
+ "@kubb/ast": "5.0.0-alpha.49"
44
48
  },
45
49
  "devDependencies": {
46
50
  "@types/ws": "^8.18.1",
@@ -48,22 +52,18 @@
48
52
  "nitropack": "^2.13.3",
49
53
  "vite": "^8.0.8",
50
54
  "@internals/utils": "0.0.0",
51
- "@kubb/adapter-oas": "5.0.0-alpha.48",
52
- "@kubb/parser-ts": "5.0.0-alpha.48"
55
+ "@kubb/adapter-oas": "5.0.0-alpha.49",
56
+ "@kubb/parser-ts": "5.0.0-alpha.49"
53
57
  },
54
58
  "engines": {
55
59
  "node": ">=22"
56
60
  },
57
- "publishConfig": {
58
- "access": "public",
59
- "registry": "https://registry.npmjs.org/"
60
- },
61
61
  "scripts": {
62
62
  "build": "nitro build",
63
63
  "clean": "npx rimraf ./dist .output",
64
- "lint": "bun biome lint .",
64
+ "lint": "oxlint .",
65
65
  "start": "node .output/server/index.mjs",
66
- "lint:fix": "bun biome lint --fix --unsafe .",
66
+ "lint:fix": "oxlint --fix .",
67
67
  "release": "pnpm publish --no-git-check",
68
68
  "release:canary": "bash ../../.github/canary.sh && node ../../scripts/build.js canary && pnpm publish --no-git-check",
69
69
  "dev": "nitro dev",