@kubb/studio 5.3.4 → 5.3.6
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.
- package/README.md +32 -11
- package/dist/index.cjs +508 -490
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +19 -5
- package/dist/index.js +472 -480
- package/dist/index.js.map +1 -1
- package/dist/protocol.cjs +33 -41
- package/dist/protocol.cjs.map +1 -1
- package/dist/protocol.d.ts +144 -220
- package/dist/protocol.js +32 -35
- package/dist/protocol.js.map +1 -1
- package/package.json +6 -4
- package/dist/rolldown-runtime-qbf5tadS.cjs +0 -38
package/dist/protocol.cjs
CHANGED
|
@@ -1,51 +1,43 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
require("./rolldown-runtime-qbf5tadS.cjs");
|
|
3
2
|
//#region src/protocol/index.ts
|
|
4
3
|
/**
|
|
5
|
-
* The
|
|
4
|
+
* The public, JSON-safe subset of Kubb lifecycle hooks. The core registry remains extensible;
|
|
5
|
+
* adding a core hook does not publish it to Studio until it is listed here and projected below.
|
|
6
6
|
*/
|
|
7
|
-
const
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
7
|
+
const generationEventTypes = [
|
|
8
|
+
"kubb:plugin:start",
|
|
9
|
+
"kubb:plugin:end",
|
|
10
|
+
"kubb:build:start",
|
|
11
|
+
"kubb:build:end",
|
|
12
|
+
"kubb:files:processing:start",
|
|
13
|
+
"kubb:files:processing:update",
|
|
14
|
+
"kubb:files:processing:end",
|
|
15
|
+
"kubb:info",
|
|
16
|
+
"kubb:success",
|
|
17
|
+
"kubb:warn",
|
|
18
|
+
"kubb:error",
|
|
19
|
+
"kubb:diagnostic",
|
|
20
|
+
"kubb:generation:start",
|
|
21
|
+
"kubb:generation:end",
|
|
22
|
+
"kubb:generation:summary",
|
|
23
|
+
"kubb:lifecycle:start",
|
|
24
|
+
"kubb:lifecycle:end",
|
|
25
|
+
"kubb:format:start",
|
|
26
|
+
"kubb:format:end",
|
|
27
|
+
"kubb:lint:start",
|
|
28
|
+
"kubb:lint:end",
|
|
29
|
+
"kubb:hooks:start",
|
|
30
|
+
"kubb:hooks:end",
|
|
31
|
+
"kubb:hook:start",
|
|
32
|
+
"kubb:hook:line",
|
|
33
|
+
"kubb:hook:end"
|
|
12
34
|
];
|
|
13
|
-
function createJobId() {
|
|
14
|
-
return crypto.randomUUID();
|
|
15
|
-
}
|
|
16
|
-
function isCommandMessage(msg) {
|
|
17
|
-
return commandTypes.includes(msg.type);
|
|
18
|
-
}
|
|
19
35
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* @example
|
|
23
|
-
* ```ts
|
|
24
|
-
* if (isDataMessage(msg, 'kubb:plugin:start')) {
|
|
25
|
-
* // msg.payload.data is now typed as [ctx: { plugin: { name: string } }]
|
|
26
|
-
* const pluginName = msg.payload.data[0].plugin.name
|
|
27
|
-
* }
|
|
28
|
-
* ```
|
|
36
|
+
* How many files a single `readFiles` request may ask for at once.
|
|
29
37
|
*/
|
|
30
|
-
|
|
31
|
-
return msg.type === "agent:data" && (type ? msg.payload.type === type : true);
|
|
32
|
-
}
|
|
33
|
-
function isStudioPongMessage(msg) {
|
|
34
|
-
return msg.type === "studio:pong";
|
|
35
|
-
}
|
|
36
|
-
function isStudioReadyMessage(msg) {
|
|
37
|
-
return msg.type === "studio:ready";
|
|
38
|
-
}
|
|
39
|
-
function isDisconnectMessage(msg) {
|
|
40
|
-
return msg.type === "studio:disconnect";
|
|
41
|
-
}
|
|
38
|
+
const MAX_FILES_PER_REQUEST = 50;
|
|
42
39
|
//#endregion
|
|
43
|
-
exports.
|
|
44
|
-
exports.
|
|
45
|
-
exports.isCommandMessage = isCommandMessage;
|
|
46
|
-
exports.isDataMessage = isDataMessage;
|
|
47
|
-
exports.isDisconnectMessage = isDisconnectMessage;
|
|
48
|
-
exports.isStudioPongMessage = isStudioPongMessage;
|
|
49
|
-
exports.isStudioReadyMessage = isStudioReadyMessage;
|
|
40
|
+
exports.MAX_FILES_PER_REQUEST = MAX_FILES_PER_REQUEST;
|
|
41
|
+
exports.generationEventTypes = generationEventTypes;
|
|
50
42
|
|
|
51
43
|
//# sourceMappingURL=protocol.cjs.map
|
package/dist/protocol.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.cjs","names":[],"sources":["../src/protocol/index.ts"],"sourcesContent":["/**\n * WebSocket message types for the agent ↔ Studio protocol. Every message name carries the side that\n * sent it, so direction reads off the name instead of the verb's tense:\n *\n * | Direction | Type | Purpose |\n * | ---------------- | --------------------- | -------------------------------------------------------------|\n * | Studio → agent | `studio:generate` | Run a generation. No dedicated reply, the result arrives as an `agent:data` message carrying `kubb:generation:end`, so it stays ordered against the rest of that run's event stream. `studio:save`/`studio:snapshot` reply directly instead, since neither needs that ordering. A CI client omits `storage` from that reply on its own, since it has no UI to render the files for. |\n * | Studio → agent | `studio:connect` | Ask the agent to resend its `agent:connect` handshake payload. |\n * | Studio → agent | `studio:save` | Edit `kubb.config.ts`. Replied to with `agent:save`. |\n * | Studio → agent | `studio:snapshot` | Pack the session's most recent generation into a tarball and upload it to a Studio path, which redirects to storage. Carries no file contents itself. Replied to with `agent:snapshot`. |\n * | Studio → agent | `studio:pong` | Reply to an `agent:ping` heartbeat. |\n * | Studio → agent | `studio:ready` | Acknowledges `agent:connect`, so the session now counts as available for job dispatch. |\n * | Studio → agent | `studio:disconnect` | The session expired or was revoked, so the agent should not reconnect. |\n * | Studio → agent | `studio:error` | A failure outside a generation, e.g. a malformed command. |\n * | Agent → Studio | `agent:connect` | Handshake sent on open and after every `studio:connect`. |\n * | Agent → Studio | `agent:save` | Reply to `studio:save`. |\n * | Agent → Studio | `agent:snapshot` | Reply to `studio:snapshot`. The tarball itself already went out to storage, so this only carries the integrity hash or an error. |\n * | Agent → Studio | `agent:data` | One generation lifecycle event, `payload.type` a {@link KubbHook}. Carries `kubb:generation:end` (the closest thing `studio:generate` has to a reply) among many others. |\n * | Agent → Studio | `agent:ping` | Heartbeat, so the connection is not treated as idle. |\n * | Agent → Studio | `agent:disconnect` | The agent is shutting down. |\n *\n * `kubb:` stays reserved for generation lifecycle, so the {@link KubbHooks} events relayed inside an\n * `agent:data` payload keep their own names. The envelope says who sent it, the payload says what\n * happened.\n */\n\nimport type { Config } from '@kubb/core'\n\n/**\n * JSON-serializable Kubb config exchanged over the WebSocket. A live `kubb/kit` config holds\n * functions and class instances that cannot survive JSON, so both sides pass this flattened shape\n * and rebuild the real config from it.\n */\nexport type JSONKubbConfig = {\n /**\n * Plugins with their serialized options. `name` is the package name (e.g. `@kubb/plugin-ts`)\n * and `options` is an opaque blob the agent forwards unchanged to the plugin factory. An entry\n * with `disabled: true` is dropped even when the disk config's `plugins` array still lists it.\n */\n plugins?: Array<{\n name: string\n options?: object\n disabled?: boolean\n }>\n /**\n * Raw OpenAPI / Swagger spec content (YAML or JSON string).\n * Always honored for a 'sandbox' agent. For a non-sandbox agent it is honored only when the\n * agent opts in with `KUBB_AGENT_ALLOW_INPUT`; otherwise the spec is read from disk and this is ignored.\n */\n input?: string\n /**\n * Adapter option overrides sent from Studio UI. Merged into the disk config's adapter options\n * and re-applied through the same adapter factory, since an adapter instance's functions\n * (`parse`, `getImports`, ...) can't survive JSON serialization over the WebSocket.\n */\n adapter?: object\n}\n\n/**\n * Which `defineConfig(...)` entry an edit targets, for a config file that exports an array.\n *\n * A number selects by position, a string matches the entry's `name`. Omitted targets the only\n * entry, or the first one when the file exports an array.\n */\nexport type ConfigRef = string | number\n\n/**\n * A value the agent can read out of a plugin option in `kubb.config.ts` and round-trip through JSON.\n */\nexport type OptionValue = string | number | boolean | null | Array<OptionValue> | { [key: string]: OptionValue }\n\n/**\n * One change to a plugin's options in the user's `kubb.config.ts`.\n *\n * `plugin` is the package name (`@kubb/plugin-ts`), the same identity used in {@link JSONKubbConfig}.\n * The agent applies these to the file with an AST patch, so only the targeted values are rewritten.\n *\n * Declared here rather than in `configFile.ts` because this is the wire contract, and the patcher\n * imports it from here. Type-only, so nothing pulls `magicast` into this entry point.\n */\nexport type ConfigEdit =\n /**\n * Write a literal option value. `path` walks nested objects, so `['enum', 'type']` targets\n * `pluginTs({ enum: { type } })`.\n */\n | { operation: 'set'; config?: ConfigRef; plugin: string; path: Array<string>; value: unknown }\n /**\n * Drop an option so the plugin falls back to its default.\n */\n | { operation: 'remove'; config?: ConfigRef; plugin: string; path: Array<string> }\n /**\n * Add a plugin factory call and its import to the `plugins` array.\n */\n | { operation: 'add-plugin'; config?: ConfigRef; plugin: string; importName?: string; options?: Record<string, unknown> }\n /**\n * Comment the plugin call out, keeping its options in the file so enabling it again restores them.\n */\n | { operation: 'disable-plugin'; config?: ConfigRef; plugin: string }\n /**\n * Uncomment a plugin call a previous `disable-plugin` commented out.\n */\n | { operation: 'enable-plugin'; config?: ConfigRef; plugin: string }\n\n/**\n * A plugin factory call the agent found in the `plugins` array of a `defineConfig(...)`.\n */\nexport type PluginView = {\n /**\n * Local identifier of the factory in the file, e.g. `pluginTs`. This is the alias when the plugin\n * was imported under one.\n */\n importName: string\n /**\n * Module the factory is imported from, e.g. `@kubb/plugin-ts`.\n */\n packageName: string\n /**\n * Top-level option keys, each flagged with whether the agent may write it and, when it can, the\n * value found in the file. An option marked `literal: false` holds a function or a reference the\n * agent will not overwrite, so Studio shows the control disabled rather than hiding it, and\n * `value` is absent since there is nothing safe to display as the current value.\n */\n options: Record<string, { literal: boolean; value?: OptionValue }>\n /**\n * Set when the plugin call is commented out in the file. Its options stay on disk but are not\n * readable, so `options` is empty until it is enabled again.\n */\n disabled?: true\n}\n\n/**\n * One `defineConfig(...)` entry. A config file that exports a single object has exactly one.\n */\nexport type ConfigView = {\n /**\n * The entry's `name`, when it sets one. Studio labels the config picker with it.\n */\n name?: string\n /**\n * Each plugin call in the entry, with its top-level option keys.\n */\n plugins: Array<PluginView>\n}\n\n/**\n * What the agent found in the user's config file, so Studio knows which controls it may offer.\n * Absent when the agent could not read the file at all.\n */\nexport type ConfigFileView =\n | {\n managed: true\n /**\n * One entry per config the file exports, in source order. Every {@link ConfigEdit} names\n * which of these it targets through its `config` field.\n */\n configs: Array<ConfigView>\n }\n | {\n managed: false\n /**\n * Why the file is outside what the agent edits, for example a default export that is not a\n * `defineConfig(...)` call. Studio shows this and offers no property-level controls.\n */\n reason: string\n }\n\n/**\n * Outcome of a single {@link ConfigEdit}, returned in a {@link ConfigSavedMessage}.\n */\nexport type ConfigEditOutcome = {\n edit: ConfigEdit\n applied: boolean\n /**\n * Why the edit was refused, absent when it was applied.\n */\n reason?: string\n}\n\n/**\n * Typed events sent by the Kubb agent to Studio over WebSocket.\n * Mirrors the single-context-object tuple style of {@link KubbHooks} in `kubb/kit`,\n * using JSON-serializable shapes (e.g. `sources` as a `Record` instead of `Map`,\n * `error` as `{ message; stack? }` instead of `Error`).\n */\nexport type KubbHooks = {\n 'kubb:plugin:start': [ctx: { plugin: { name: string } }]\n 'kubb:plugin:end': [ctx: { plugin: { name: string }; duration: number; success: boolean }]\n 'kubb:build:start': [ctx: { config: { name?: string }; adapter: { name: string } }]\n 'kubb:build:end': [ctx: { files: Array<{ path: string; name: string }>; outputDir: string }]\n 'kubb:files:processing:start': [ctx: { total: number }]\n 'kubb:files:processing:update': [\n ctx: {\n files: Array<{\n file: string\n processed: number\n total: number\n percentage: number\n }>\n },\n ]\n 'kubb:files:processing:end': [ctx: { total: number }]\n 'kubb:info': [ctx: { message: string; info?: string }]\n 'kubb:success': [ctx: { message: string; info?: string }]\n 'kubb:warn': [ctx: { message: string; info?: string }]\n 'kubb:error': [ctx: { message: string; stack?: string }]\n 'kubb:debug': [ctx: { logs: Array<string>; fileName?: string }]\n 'kubb:generation:start': [ctx: { name?: string; plugins: number }]\n 'kubb:generation:end': [\n ctx: {\n config: Config\n storage: Record<string, string>\n peerDependencies: Record<string, string>\n missingDependencies: Array<string>\n },\n ]\n 'kubb:generation:summary': [ctx: { duration: number; fileCount: number; failedPlugins: number; status: 'success' | 'failed' }]\n 'kubb:lifecycle:start': []\n 'kubb:lifecycle:end': []\n 'kubb:format:start': []\n 'kubb:format:end': []\n 'kubb:lint:start': []\n 'kubb:lint:end': []\n 'kubb:hooks:start': []\n 'kubb:hooks:end': []\n 'kubb:hook:start': [ctx: { id?: string; command: string; args?: Array<string> }]\n 'kubb:hook:line': [ctx: { id: string; line: string }]\n 'kubb:hook:end': [\n ctx: {\n id?: string\n command: string\n args?: Array<string>\n success: boolean\n error?: { message: string; stack?: string }\n },\n ]\n}\n\nexport type KubbHook = keyof KubbHooks\n\n/**\n * Run a generation with the given config. `payload` is the merged config Studio wants generated.\n */\nexport type StudioGenerateMessage = {\n type: 'studio:generate'\n jobId: string\n payload: JSONKubbConfig\n}\n\n/**\n * Ask the agent to send a fresh `agent:connect` payload. Permissions are fixed when the host starts\n * the agent; this message only triggers another read of disk config and saved Studio state.\n */\nexport type StudioConnectMessage = {\n type: 'studio:connect'\n jobId?: string\n /**\n * Version of the Studio instance asking, which refreshes what the agent picked up when the\n * session was created. Absent when Studio predates the field.\n */\n version?: string\n}\n\n/**\n * Change plugin options in the user's `kubb.config.ts`. Applied only when the agent was granted\n * `allowConfigEdit`; otherwise every edit comes back refused.\n */\nexport type StudioSaveMessage = {\n type: 'studio:save'\n jobId: string\n edits: Array<ConfigEdit>\n}\n\n/**\n * Ask the agent to pack a previous generation's files into an npm-installable tarball and upload\n * it to Studio directly, rather than returning the bytes over this socket. Packs the files the\n * session's own most recent `studio:generate` produced, so it only works right after that\n * generation and never carries file contents itself. Refused for a sandbox agent, since it holds\n * no generated files worth packing, and refused when no prior generation exists to pack.\n */\nexport type StudioSnapshotMessage = {\n type: 'studio:snapshot'\n jobId: string\n payload: {\n name: string\n version: string\n peerDependencies?: Record<string, string>\n /**\n * Studio path the agent `PUT`s the finished tarball to. Studio answers with a redirect to\n * storage, so the storage URL stays out of this message.\n */\n uploadPath: string\n }\n}\n\n/**\n * Anything Studio asks the agent to do. Each command is its own `type`, so a handler switches once\n * instead of reading a `type` and then a nested `command` field.\n */\nexport type CommandMessage = StudioGenerateMessage | StudioConnectMessage | StudioSaveMessage | StudioSnapshotMessage\n\n/**\n * The command names, for a host that needs the list rather than the union.\n */\nexport const commandTypes = ['studio:generate', 'studio:connect', 'studio:save', 'studio:snapshot'] as const\n\nexport function createJobId(): string {\n return crypto.randomUUID()\n}\n\n/**\n * Identifies the host running the Kubb runtime. Local to the runtime rather than part of the wire:\n * it picks which remedy a refused-input warning suggests, since the Docker agent and the CLI grant\n * `allowInput` different ways.\n */\nexport type ClientInfo = {\n /**\n * `cli` for a `kubb studio` connection from a developer's machine, `docker` for the agent image,\n * `ci` for a client embedded directly in an automated pipeline (no `kubb studio` process).\n */\n kind: 'cli' | 'docker' | 'ci'\n}\n\n/**\n * Payload of the `agent:connect` handshake, sent when the agent attaches to a session. Carries only\n * what Studio renders, with everything about the config under one key.\n */\nexport type ConnectMessagePayload = {\n /**\n * Always sent, so a mismatch is visible on both sides: Studio badges the connection with these\n * and the host prints them.\n */\n versions: {\n /**\n * The version of the `@kubb/studio` runtime the agent runs.\n */\n kubb: string\n /**\n * The version of the host itself (the `kubb.agent` package or the `kubb` CLI).\n */\n agent: string\n }\n /**\n * The agent's project root (`KUBB_AGENT_ROOT`, or the working directory when unset). This is the\n * workspace that generation runs against.\n */\n root: string\n /**\n * The baseline every generation starts from.\n */\n config: {\n /**\n * The config path as configured (`KUBB_AGENT_CONFIG`), relative to `root` unless absolute.\n */\n path: string\n /**\n * What the agent read out of the config file itself, so Studio can render the plugin editor\n * against the real file. Absent when the agent could not read it, or was not granted\n * `allowConfigEdit`.\n */\n file?: ConfigFileView\n /**\n * Plugins the config registers, with their serialized options.\n */\n plugins?: Array<{\n name: string\n options?: object\n }>\n }\n permissions: AgentPermissions\n}\n\n/**\n * What an agent may do in the project it serves. Every one is off unless the host granted it, and\n * a sandbox session narrows them further.\n */\nexport type AgentPermissions = {\n /**\n * Whether the agent writes generated files to disk. False for a sandbox agent. For a local\n * agent it mirrors the agent's `KUBB_AGENT_ALLOW_WRITE`.\n */\n allowWrite: boolean\n /**\n * Whether the agent will accept and generate from an OpenAPI spec supplied by Studio.\n * Always true for a sandbox agent, otherwise it mirrors the agent's own opt-in. Studio reads\n * this to decide whether to send `input`.\n */\n allowInput: boolean\n /**\n * Whether the agent runs the formatter, the linter, and `output.postGenerate` as child\n * processes after a generation. Always true for the Docker agent, where the image bounds what\n * can run. The CLI runs in the user's own project and defaults it off.\n */\n allowExec: boolean\n /**\n * Whether the agent may change plugin options in the user's `kubb.config.ts`. Separate from\n * `allowWrite`, which covers generated output: this one edits a hand-authored source file.\n */\n allowConfigEdit: boolean\n}\n\n/**\n * Agent → Studio handshake. Sent when the WebSocket opens and again after a `connect` command.\n * Carries the on-disk config baseline, granted permissions, and paths Studio needs to render the editor.\n */\nexport type AgentConnectMessage = {\n type: 'agent:connect'\n payload: ConnectMessagePayload\n}\n\n/**\n * Reply to a `save` command: what the agent did to the file on disk.\n */\nexport type AgentSaveMessage = {\n type: 'agent:save'\n jobId: string\n payload: {\n /**\n * Per-edit result, in the order the edits were sent.\n */\n outcomes: Array<ConfigEditOutcome>\n /**\n * Whether the file on disk changed. False when every edit was refused, and when the applied\n * edits produced the text the file already had.\n */\n changed: boolean\n /**\n * The config file as it now stands, so Studio can re-render without a round trip. Absent when\n * nothing was written. Named to match `config.file` in the connect payload.\n */\n file?: ConfigFileView\n }\n}\n\n/**\n * Reply to `studio:snapshot`. The tarball already went to storage, so this only reports whether\n * the upload succeeded.\n */\nexport type AgentSnapshotMessage = {\n type: 'agent:snapshot'\n jobId: string\n payload: { status: 'ok'; integrity: string } | { status: 'error'; message: string }\n}\n\n/**\n * Failure notice from Studio for something that breaks outside a generation, such as a malformed\n * command. The agent's own failures travel as an `agent:data` message carrying a `kubb:error`\n * payload, which keeps them ordered against the generation events around them.\n */\nexport type StudioErrorMessage = {\n type: 'studio:error'\n message: string\n}\n\n/**\n * Heartbeat sent by the Agent to Studio so the connection is not treated as idle.\n */\nexport type AgentPingMessage = {\n type: 'agent:ping'\n}\n\n/**\n * Studio's reply to an `agent:ping`, confirming the connection is still alive.\n */\nexport type StudioPongMessage = {\n type: 'studio:pong'\n}\n\n/**\n * Studio's acknowledgement that an `agent:connect` handshake was received and the session is\n * fully registered: the connection now counts as available for job dispatch. Distinct from the\n * socket merely being open, which is not yet the same thing.\n */\nexport type StudioReadyMessage = {\n type: 'studio:ready'\n}\n\n/**\n * Disconnect message sent from Studio to Agent when the session is expired or revoked.\n * The agent should close the connection without reconnecting.\n */\nexport type StudioDisconnectMessage = {\n type: 'studio:disconnect'\n reason: 'expired' | 'revoked'\n}\n\n/**\n * The agent going away, so Studio marks the session offline instead of waiting out the heartbeat\n * window. The mirror of {@link StudioDisconnectMessage}.\n *\n * Only sent for a shutdown. An expired or revoked session was Studio's own decision, so echoing it\n * back says nothing new.\n */\nexport type AgentDisconnectMessage = {\n type: 'agent:disconnect'\n reason: 'shutdown'\n}\n\n/**\n * Payload of an `agent:data` message: a single Kubb generation event forwarded to Studio in real time.\n * Generic over the hook name so `data` is typed to that hook's context tuple.\n */\nexport type DataMessagePayload<T extends KubbHook = KubbHook> = {\n /**\n * The Kubb hook this event is for (e.g. `kubb:plugin:start`).\n */\n type: T\n /**\n * The hook's context tuple, matching `KubbHooks[type]`.\n */\n data: KubbHooks[T]\n /**\n * When the agent emitted the event, epoch milliseconds.\n */\n timestamp: number\n /**\n * Monotonic per-connection counter stamped in the order the agent emits events. Studio orders the\n * event log by this, since `timestamp` has millisecond resolution and a full generation fires\n * dozens of events per tick, and the relay can deliver them out of order.\n */\n seq: number\n}\n\n/**\n * Envelope for a single generation event streamed from Agent to Studio. Wraps a\n * {@link DataMessagePayload} so both sides can switch on `type: 'agent:data'`.\n */\nexport type DataMessage<T extends KubbHook = KubbHook> = {\n type: 'agent:data'\n jobId: string\n payload: DataMessagePayload<T>\n}\n\n/**\n * Response returned by the Studio `/api/agent/sessions` endpoint.\n */\nexport type AgentConnectResponse = {\n /**\n * WebSocket URL the agent opens to reach the session, with the session token embedded.\n */\n wsUrl: string\n /**\n * When the session expires and the wsUrl stops working (ISO 8601).\n */\n expiresAt: string\n /**\n * When the session was revoked (ISO 8601), or null while it is still valid.\n */\n revokedAt: string | null\n /**\n * Opaque session token, also embedded in `wsUrl`. Store it to revoke the session later.\n */\n sessionId: string\n /**\n * Short readable identifier for this connection, used in logs (e.g. brave-otter).\n */\n slug: string | null\n /**\n * Whether this session belongs to a shared sandbox agent rather than an owned one.\n */\n isSandbox: boolean\n /**\n * The Studio instance's own version. Reported here rather than only on `studio:connect`, so the\n * agent knows it before it announces itself and can name both sides from the first connect.\n * Absent when Studio predates the field.\n */\n version?: string\n}\n\n/**\n * Every message that can cross the agent WebSocket, in either direction. Narrow it with the\n * `is*Message` guards below before reading a variant's fields.\n */\nexport type AgentMessage =\n | CommandMessage\n | DataMessage\n | AgentConnectMessage\n | AgentSaveMessage\n | AgentSnapshotMessage\n | AgentPingMessage\n | AgentDisconnectMessage\n | StudioErrorMessage\n | StudioPongMessage\n | StudioReadyMessage\n | StudioDisconnectMessage\n\nexport function isCommandMessage(msg: AgentMessage): msg is CommandMessage {\n return (commandTypes as ReadonlyArray<string>).includes(msg.type)\n}\n\n/**\n * Type guard to narrow a data message to a specific event type.\n *\n * @example\n * ```ts\n * if (isDataMessage(msg, 'kubb:plugin:start')) {\n * // msg.payload.data is now typed as [ctx: { plugin: { name: string } }]\n * const pluginName = msg.payload.data[0].plugin.name\n * }\n * ```\n */\nexport function isDataMessage<T extends KubbHook>(msg: AgentMessage, type?: T): msg is DataMessage<T> {\n return msg.type === 'agent:data' && (type ? msg.payload.type === type : true)\n}\n\nexport function isStudioPongMessage(msg: AgentMessage): msg is StudioPongMessage {\n return msg.type === 'studio:pong'\n}\n\nexport function isStudioReadyMessage(msg: AgentMessage): msg is StudioReadyMessage {\n return msg.type === 'studio:ready'\n}\n\nexport function isDisconnectMessage(msg: AgentMessage): msg is StudioDisconnectMessage {\n return msg.type === 'studio:disconnect'\n}\n"],"mappings":";;;;;;AA+SA,MAAa,eAAe;CAAC;CAAmB;CAAkB;CAAe;AAAiB;AAElG,SAAgB,cAAsB;CACpC,OAAO,OAAO,WAAW;AAC3B;AAsRA,SAAgB,iBAAiB,KAA0C;CACzE,OAAQ,aAAuC,SAAS,IAAI,IAAI;AAClE;;;;;;;;;;;;AAaA,SAAgB,cAAkC,KAAmB,MAAiC;CACpG,OAAO,IAAI,SAAS,iBAAiB,OAAO,IAAI,QAAQ,SAAS,OAAO;AAC1E;AAEA,SAAgB,oBAAoB,KAA6C;CAC/E,OAAO,IAAI,SAAS;AACtB;AAEA,SAAgB,qBAAqB,KAA8C;CACjF,OAAO,IAAI,SAAS;AACtB;AAEA,SAAgB,oBAAoB,KAAmD;CACrF,OAAO,IAAI,SAAS;AACtB"}
|
|
1
|
+
{"version":3,"file":"protocol.cjs","names":[],"sources":["../src/protocol/index.ts"],"sourcesContent":["import type { KubbHooks } from '@kubb/core'\n\n/**\n * JSON-serializable Kubb config exchanged over RPC. A live `kubb/kit` config holds\n * functions and class instances that cannot survive JSON, so both sides pass this flattened shape\n * and rebuild the real config from it.\n */\nexport type JSONKubbConfig = {\n /**\n * Plugins with their serialized options. `name` is the package name (e.g. `@kubb/plugin-ts`)\n * and `options` is an opaque blob the agent forwards unchanged to the plugin factory. An entry\n * with `disabled: true` is dropped even when the disk config's `plugins` array still lists it.\n */\n plugins?: Array<{\n name: string\n options?: object\n disabled?: boolean\n }>\n /**\n * Raw OpenAPI / Swagger spec content (YAML or JSON string).\n * Always honored for a 'sandbox' agent. For a non-sandbox agent it is honored only when the\n * agent opts in with `KUBB_AGENT_ALLOW_INPUT`; otherwise the spec is read from disk and this is ignored.\n */\n input?: string\n /**\n * Adapter option overrides sent from Studio UI. Merged into the disk config's adapter options\n * and re-applied through the same adapter factory, since an adapter instance's functions\n * (`parse`, `getImports`, ...) can't survive JSON serialization over the WebSocket.\n */\n adapter?: object\n}\n\n/**\n * Which `defineConfig(...)` entry an edit targets, for a config file that exports an array.\n *\n * A number selects by position, a string matches the entry's `name`. Omitted targets the only\n * entry, or the first one when the file exports an array.\n */\nexport type ConfigRef = string | number\n\n/**\n * A value the agent can read out of a plugin option in `kubb.config.ts` and round-trip through JSON.\n */\nexport type OptionValue = string | number | boolean | null | Array<OptionValue> | { [key: string]: OptionValue }\n\n/**\n * One change to a plugin's options in the user's `kubb.config.ts`.\n *\n * `plugin` is the package name (`@kubb/plugin-ts`), the same identity used in {@link JSONKubbConfig}.\n * The agent applies these to the file with an AST patch, so only the targeted values are rewritten.\n *\n * Declared here rather than in `configFile.ts` because this is the wire contract, and the patcher\n * imports it from here. Type-only, so nothing pulls `magicast` into this entry point.\n */\nexport type ConfigEdit =\n /**\n * Write a literal option value. `path` walks nested objects, so `['enum', 'type']` targets\n * `pluginTs({ enum: { type } })`.\n */\n | { operation: 'set'; config?: ConfigRef; plugin: string; path: Array<string>; value: unknown }\n /**\n * Drop an option so the plugin falls back to its default.\n */\n | { operation: 'remove'; config?: ConfigRef; plugin: string; path: Array<string> }\n /**\n * Add a plugin factory call and its import to the `plugins` array.\n */\n | { operation: 'add-plugin'; config?: ConfigRef; plugin: string; importName?: string; options?: Record<string, unknown> }\n /**\n * Comment the plugin call out, keeping its options in the file so enabling it again restores them.\n */\n | { operation: 'disable-plugin'; config?: ConfigRef; plugin: string }\n /**\n * Uncomment a plugin call a previous `disable-plugin` commented out.\n */\n | { operation: 'enable-plugin'; config?: ConfigRef; plugin: string }\n\n/**\n * A plugin factory call the agent found in the `plugins` array of a `defineConfig(...)`.\n */\nexport type PluginView = {\n /**\n * Local identifier of the factory in the file, e.g. `pluginTs`. This is the alias when the plugin\n * was imported under one.\n */\n importName: string\n /**\n * Module the factory is imported from, e.g. `@kubb/plugin-ts`.\n */\n packageName: string\n /**\n * Top-level option keys, each flagged with whether the agent may write it and, when it can, the\n * value found in the file. An option marked `literal: false` holds a function or a reference the\n * agent will not overwrite, so Studio shows the control disabled rather than hiding it, and\n * `value` is absent since there is nothing safe to display as the current value.\n */\n options: Record<string, { literal: boolean; value?: OptionValue }>\n /**\n * Set when the plugin call is commented out in the file. Its options stay on disk but are not\n * readable, so `options` is empty until it is enabled again.\n */\n disabled?: true\n}\n\n/**\n * One `defineConfig(...)` entry. A config file that exports a single object has exactly one.\n */\nexport type ConfigView = {\n /**\n * The entry's `name`, when it sets one. Studio labels the config picker with it.\n */\n name?: string\n /**\n * Each plugin call in the entry, with its top-level option keys.\n */\n plugins: Array<PluginView>\n}\n\n/**\n * What the agent found in the user's config file, so Studio knows which controls it may offer.\n * Absent when the agent could not read the file at all.\n */\nexport type ConfigFileView =\n | {\n managed: true\n /**\n * One entry per config the file exports, in source order. Every {@link ConfigEdit} names\n * which of these it targets through its `config` field.\n */\n configs: Array<ConfigView>\n }\n | {\n managed: false\n /**\n * Why the file is outside what the agent edits, for example a default export that is not a\n * `defineConfig(...)` call. Studio shows this and offers no property-level controls.\n */\n reason: string\n }\n\n/**\n * Outcome of a single {@link ConfigEdit}, returned in a {@link ConfigSavedMessage}.\n */\nexport type ConfigEditOutcome = {\n edit: ConfigEdit\n applied: boolean\n /**\n * Why the edit was refused, absent when it was applied.\n */\n reason?: string\n}\n\n/**\n * The public, JSON-safe subset of Kubb lifecycle hooks. The core registry remains extensible;\n * adding a core hook does not publish it to Studio until it is listed here and projected below.\n */\nexport const generationEventTypes = [\n 'kubb:plugin:start',\n 'kubb:plugin:end',\n 'kubb:build:start',\n 'kubb:build:end',\n 'kubb:files:processing:start',\n 'kubb:files:processing:update',\n 'kubb:files:processing:end',\n 'kubb:info',\n 'kubb:success',\n 'kubb:warn',\n 'kubb:error',\n 'kubb:diagnostic',\n 'kubb:generation:start',\n 'kubb:generation:end',\n 'kubb:generation:summary',\n 'kubb:lifecycle:start',\n 'kubb:lifecycle:end',\n 'kubb:format:start',\n 'kubb:format:end',\n 'kubb:lint:start',\n 'kubb:lint:end',\n 'kubb:hooks:start',\n 'kubb:hooks:end',\n 'kubb:hook:start',\n 'kubb:hook:line',\n 'kubb:hook:end',\n] as const satisfies ReadonlyArray<keyof KubbHooks>\n\n/**\n * One of the lifecycle hooks {@link generationEventTypes} publishes.\n */\nexport type GenerationEventType = (typeof generationEventTypes)[number]\n\n/**\n * The JSON-safe payload each published event carries. These are flattened on purpose: a core hook\n * context holds live objects (a `Config`, a `Storage`) that cannot cross the wire.\n */\nexport type GenerationEventPayloads = {\n 'kubb:plugin:start': [ctx: { plugin: { name: string } }]\n 'kubb:plugin:end': [ctx: { plugin: { name: string }; duration: number; success: boolean }]\n 'kubb:build:start': [ctx: { config: { name?: string }; adapter: { name: string } }]\n 'kubb:build:end': [ctx: { files: Array<{ path: string; name: string }>; outputDir: string }]\n 'kubb:files:processing:start': [ctx: { total: number }]\n 'kubb:files:processing:update': [\n ctx: {\n files: Array<{\n file: string\n processed: number\n total: number\n percentage: number\n }>\n },\n ]\n 'kubb:files:processing:end': [ctx: { total: number }]\n 'kubb:info': [ctx: { message: string; info?: string }]\n 'kubb:success': [ctx: { message: string; info?: string }]\n 'kubb:warn': [ctx: { message: string; info?: string }]\n 'kubb:error': [ctx: { message: string; stack?: string }]\n 'kubb:diagnostic': [\n ctx: {\n code: string\n message: string\n severity: string\n location?: { kind: string; pointer?: string; ref?: string }\n help?: string\n plugin?: string\n stack?: string\n },\n ]\n 'kubb:generation:start': [ctx: { name?: string; plugins: number }]\n /**\n * A run finished. See `kubb:build:end` for files, `kubb:generation:summary` for the count, and\n * `readFiles` for contents.\n */\n 'kubb:generation:end': []\n 'kubb:generation:summary': [ctx: { duration: number; fileCount: number; failedPlugins: number; status: 'success' | 'failed' }]\n 'kubb:lifecycle:start': []\n 'kubb:lifecycle:end': []\n 'kubb:format:start': []\n 'kubb:format:end': []\n 'kubb:lint:start': []\n 'kubb:lint:end': []\n 'kubb:hooks:start': []\n 'kubb:hooks:end': []\n 'kubb:hook:start': [ctx: { id?: string; command: string; args?: Array<string> }]\n 'kubb:hook:line': [ctx: { id: string; line: string }]\n 'kubb:hook:end': [\n ctx: {\n id?: string\n command: string\n args?: Array<string>\n success: boolean\n error?: { message: string; stack?: string }\n },\n ]\n}\n\n/**\n * Versioned envelope around one lifecycle event. Cap'n Web streams preserve the order the agent\n * emitted them in, so the receiver replays a run by reading the stream straight through.\n */\nexport type GenerationEvent = {\n [Type in GenerationEventType]: { type: Type; data: GenerationEventPayloads[Type] }\n}[GenerationEventType] & {\n version: 1\n jobId: string\n timestamp: number\n}\n\n/**\n * Asks the agent to run one generation. `jobId` tags every event the run emits so a caller\n * watching several runs can tell them apart.\n */\nexport type GenerateInput = { jobId: string; config: JSONKubbConfig }\n\n/**\n * What a finished run produced. `files` holds paths relative to the output directory.\n */\nexport type GenerateResult = { status: 'success' | 'failed'; files: Array<string>; fileCount: number }\n\n/**\n * Asks the agent to apply a batch of edits to the config file on disk.\n */\nexport type SaveConfigInput = { edits: Array<ConfigEdit> }\n\n/**\n * Per-edit outcomes plus the rewritten file. `changed` is false when every edit was a no-op, so a\n * caller can skip reloading.\n */\nexport type SaveResult = { outcomes: Array<ConfigEditOutcome>; changed: boolean; file?: ConfigFileView }\n\n/**\n * Asks the agent to read generated files back. Capped at {@link MAX_FILES_PER_REQUEST} paths, all\n * of which must sit inside the output directory.\n */\nexport type ReadFilesInput = { paths: Array<string> }\n\n/**\n * Describes the package to pack and where to PUT it. `uploadPath` is resolved against the Studio\n * origin, so it cannot redirect the upload elsewhere.\n */\nexport type PublishSnapshotInput = { name: string; version: string; bundledDependencies?: Array<string>; uploadPath: string }\n\n/**\n * Identifies the uploaded snapshot. `integrity` is the subresource hash Studio verifies against.\n */\nexport type PublishSnapshotResult = { integrity: string; peerDependencies: Record<string, string> }\n\n/**\n * A generation in flight. Cap'n Web keeps the three calls pointed at the same run, so a caller can\n * read `events()` while `result()` is still pending and `cancel()` stops it early.\n */\nexport type GenerationRun = {\n events: () => Promise<ReadableStream<GenerationEvent>>\n result: () => Promise<GenerateResult>\n cancel: () => Promise<void>\n}\n\n/**\n * Operations Studio can invoke on an agent through a host-provided RPC transport.\n */\nexport type AgentApi = {\n connect: () => Promise<ConnectMessagePayload>\n startGeneration: (input: GenerateInput) => GenerationRun\n saveConfig: (input: SaveConfigInput) => Promise<SaveResult>\n publishSnapshot: (input: PublishSnapshotInput) => Promise<PublishSnapshotResult>\n readFiles: (input: ReadFilesInput) => Promise<{ files: Record<string, string> }>\n}\n\n/**\n * Operations an agent can invoke on Studio through a host-provided RPC transport.\n */\nexport type StudioApi = {\n ping: () => Promise<void>\n}\n\n/**\n * A live RPC session. `closed` settles when the transport drops, whichever side ended it.\n */\nexport type RpcConnection = {\n studio: StudioApi\n closed: Promise<void>\n close: () => void\n}\n\n/**\n * Opens a transport and hands both sides their peer. Swapping this is how a test drives a session\n * without a socket.\n */\nexport type RpcConnector = (input: { url: string; token: string; local: AgentApi }) => Promise<RpcConnection>\n\n/**\n * How many files a single `readFiles` request may ask for at once.\n */\nexport const MAX_FILES_PER_REQUEST = 50\n\n/**\n * Identifies the host running the Kubb runtime. Local to the runtime, not part of the wire: it\n * picks which remedy a refused-permission warning names. Distinct from an agent's `type` (`user`,\n * `cli`, `ci`, `sandbox`, `global`), which is what Studio records the agent as at pairing.\n */\nexport type ClientInfo = {\n /**\n * `cli` for any `kubb` invocation, including `kubb studio snapshot` from CI. `docker` for the\n * agent image.\n */\n kind: 'cli' | 'docker'\n}\n\n/**\n * Connection payload returned by {@link AgentApi.connect}. Carries only what Studio renders, with\n * everything about the config under one key.\n */\nexport type ConnectMessagePayload = {\n /**\n * Always sent, so a mismatch is visible on both sides: Studio badges the connection with these\n * and the host prints them.\n */\n versions: {\n /**\n * The version of the `@kubb/studio` runtime the agent runs.\n */\n kubb: string\n /**\n * The version of the host itself (the `kubb.agent` package or the `kubb` CLI).\n */\n agent: string\n }\n /**\n * The agent's project root (`KUBB_AGENT_ROOT`, or the working directory when unset). This is the\n * workspace that generation runs against.\n */\n root: string\n /**\n * The baseline every generation starts from.\n */\n config: {\n /**\n * The config path as configured (`KUBB_AGENT_CONFIG`), relative to `root` unless absolute.\n */\n path: string\n /**\n * What the agent read out of the config file itself, so Studio can render the plugin editor\n * against the real file. Absent when the agent could not read it, or was not granted\n * `allowConfigEdit`.\n */\n file?: ConfigFileView\n /**\n * Plugins the config registers, with their serialized options.\n */\n plugins?: Array<{\n name: string\n options?: object\n }>\n }\n permissions: AgentPermissions\n}\n\n/**\n * What an agent may do in the project it serves. Every one is off unless the host granted it, and\n * a sandbox session narrows them further.\n */\nexport type AgentPermissions = {\n /**\n * Whether the agent writes generated files to disk. False for a sandbox agent. For a local\n * agent it mirrors the agent's `KUBB_AGENT_ALLOW_WRITE`.\n */\n allowWrite: boolean\n /**\n * Whether the agent will accept and generate from an OpenAPI spec supplied by Studio.\n * Always true for a sandbox agent, otherwise it mirrors the agent's own opt-in. Studio reads\n * this to decide whether to send `input`.\n */\n allowInput: boolean\n /**\n * Whether the agent runs the formatter, the linter, and `output.postGenerate` as child\n * processes after a generation. Always true for the Docker agent, where the image bounds what\n * can run. The CLI runs in the user's own project and defaults it off.\n */\n allowExec: boolean\n /**\n * Whether the agent may change plugin options in the user's `kubb.config.ts`. Separate from\n * `allowWrite`, which covers generated output: this one edits a hand-authored source file.\n */\n allowConfigEdit: boolean\n /**\n * Whether the agent hands back file source in response to `readFiles`. Always true for a\n * sandbox agent; for a local agent it mirrors the agent's own opt-in.\n */\n allowRead: boolean\n}\n\n/**\n * Response returned by the Studio `/api/agent/sessions` endpoint.\n */\nexport type AgentConnectResponse = {\n /**\n * URL the agent opens to reach the session, with the session token embedded.\n */\n url: string\n /**\n * When the session expires and the url stops working (ISO 8601).\n */\n expiresAt: string\n /**\n * When the session was revoked (ISO 8601), or null while it is still valid.\n */\n revokedAt: string | null\n /**\n * Opaque session token, also embedded in `url`. Store it to revoke the session later.\n */\n sessionId: string\n /**\n * Short readable identifier for this connection, used in logs (e.g. brave-otter).\n */\n slug: string | null\n /**\n * Whether this session belongs to a shared sandbox agent rather than an owned one.\n */\n isSandbox: boolean\n /**\n * The Studio instance's own version. Returned with the RPC session so the agent can name both\n * sides from the first connection.\n * Absent when Studio predates the field.\n */\n version?: string\n}\n"],"mappings":";;;;;;AA4JA,MAAa,uBAAuB;CAClC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;AAwKA,MAAa,wBAAwB"}
|
package/dist/protocol.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { t as __name } from "./rolldown-runtime-CRm0XQPb.js";
|
|
2
|
-
import { Config } from "@kubb/core";
|
|
3
2
|
//#region src/protocol/index.d.ts
|
|
4
3
|
/**
|
|
5
|
-
* JSON-serializable Kubb config exchanged over
|
|
4
|
+
* JSON-serializable Kubb config exchanged over RPC. A live `kubb/kit` config holds
|
|
6
5
|
* functions and class instances that cannot survive JSON, so both sides pass this flattened shape
|
|
7
6
|
* and rebuild the real config from it.
|
|
8
7
|
*/
|
|
@@ -172,12 +171,19 @@ export type ConfigEditOutcome = {
|
|
|
172
171
|
reason?: string;
|
|
173
172
|
};
|
|
174
173
|
/**
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
* using JSON-serializable shapes (e.g. `sources` as a `Record` instead of `Map`,
|
|
178
|
-
* `error` as `{ message; stack? }` instead of `Error`).
|
|
174
|
+
* The public, JSON-safe subset of Kubb lifecycle hooks. The core registry remains extensible;
|
|
175
|
+
* adding a core hook does not publish it to Studio until it is listed here and projected below.
|
|
179
176
|
*/
|
|
180
|
-
export
|
|
177
|
+
export declare const generationEventTypes: readonly ["kubb:plugin:start", "kubb:plugin:end", "kubb:build:start", "kubb:build:end", "kubb:files:processing:start", "kubb:files:processing:update", "kubb:files:processing:end", "kubb:info", "kubb:success", "kubb:warn", "kubb:error", "kubb:diagnostic", "kubb:generation:start", "kubb:generation:end", "kubb:generation:summary", "kubb:lifecycle:start", "kubb:lifecycle:end", "kubb:format:start", "kubb:format:end", "kubb:lint:start", "kubb:lint:end", "kubb:hooks:start", "kubb:hooks:end", "kubb:hook:start", "kubb:hook:line", "kubb:hook:end"];
|
|
178
|
+
/**
|
|
179
|
+
* One of the lifecycle hooks {@link generationEventTypes} publishes.
|
|
180
|
+
*/
|
|
181
|
+
export type GenerationEventType = (typeof generationEventTypes)[number];
|
|
182
|
+
/**
|
|
183
|
+
* The JSON-safe payload each published event carries. These are flattened on purpose: a core hook
|
|
184
|
+
* context holds live objects (a `Config`, a `Storage`) that cannot cross the wire.
|
|
185
|
+
*/
|
|
186
|
+
export type GenerationEventPayloads = {
|
|
181
187
|
'kubb:plugin:start': [ctx: {
|
|
182
188
|
plugin: {
|
|
183
189
|
name: string;
|
|
@@ -235,20 +241,28 @@ export type KubbHooks = {
|
|
|
235
241
|
message: string;
|
|
236
242
|
stack?: string;
|
|
237
243
|
}];
|
|
238
|
-
'kubb:
|
|
239
|
-
|
|
240
|
-
|
|
244
|
+
'kubb:diagnostic': [ctx: {
|
|
245
|
+
code: string;
|
|
246
|
+
message: string;
|
|
247
|
+
severity: string;
|
|
248
|
+
location?: {
|
|
249
|
+
kind: string;
|
|
250
|
+
pointer?: string;
|
|
251
|
+
ref?: string;
|
|
252
|
+
};
|
|
253
|
+
help?: string;
|
|
254
|
+
plugin?: string;
|
|
255
|
+
stack?: string;
|
|
241
256
|
}];
|
|
242
257
|
'kubb:generation:start': [ctx: {
|
|
243
258
|
name?: string;
|
|
244
259
|
plugins: number;
|
|
245
260
|
}];
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}];
|
|
261
|
+
/**
|
|
262
|
+
* A run finished. See `kubb:build:end` for files, `kubb:generation:summary` for the count, and
|
|
263
|
+
* `readFiles` for contents.
|
|
264
|
+
*/
|
|
265
|
+
'kubb:generation:end': [];
|
|
252
266
|
'kubb:generation:summary': [ctx: {
|
|
253
267
|
duration: number;
|
|
254
268
|
fileCount: number;
|
|
@@ -283,83 +297,136 @@ export type KubbHooks = {
|
|
|
283
297
|
};
|
|
284
298
|
}];
|
|
285
299
|
};
|
|
286
|
-
export type KubbHook = keyof KubbHooks;
|
|
287
300
|
/**
|
|
288
|
-
*
|
|
301
|
+
* Versioned envelope around one lifecycle event. Cap'n Web streams preserve the order the agent
|
|
302
|
+
* emitted them in, so the receiver replays a run by reading the stream straight through.
|
|
289
303
|
*/
|
|
290
|
-
export type
|
|
291
|
-
type:
|
|
304
|
+
export type GenerationEvent = { [Type in GenerationEventType]: {
|
|
305
|
+
type: Type;
|
|
306
|
+
data: GenerationEventPayloads[Type];
|
|
307
|
+
}; }[GenerationEventType] & {
|
|
308
|
+
version: 1;
|
|
292
309
|
jobId: string;
|
|
293
|
-
|
|
310
|
+
timestamp: number;
|
|
294
311
|
};
|
|
295
312
|
/**
|
|
296
|
-
*
|
|
297
|
-
*
|
|
313
|
+
* Asks the agent to run one generation. `jobId` tags every event the run emits so a caller
|
|
314
|
+
* watching several runs can tell them apart.
|
|
298
315
|
*/
|
|
299
|
-
export type
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* Version of the Studio instance asking, which refreshes what the agent picked up when the
|
|
304
|
-
* session was created. Absent when Studio predates the field.
|
|
305
|
-
*/
|
|
306
|
-
version?: string;
|
|
316
|
+
export type GenerateInput = {
|
|
317
|
+
jobId: string;
|
|
318
|
+
config: JSONKubbConfig;
|
|
307
319
|
};
|
|
308
320
|
/**
|
|
309
|
-
*
|
|
310
|
-
* `allowConfigEdit`; otherwise every edit comes back refused.
|
|
321
|
+
* What a finished run produced. `files` holds paths relative to the output directory.
|
|
311
322
|
*/
|
|
312
|
-
export type
|
|
313
|
-
|
|
314
|
-
|
|
323
|
+
export type GenerateResult = {
|
|
324
|
+
status: 'success' | 'failed';
|
|
325
|
+
files: Array<string>;
|
|
326
|
+
fileCount: number;
|
|
327
|
+
};
|
|
328
|
+
/**
|
|
329
|
+
* Asks the agent to apply a batch of edits to the config file on disk.
|
|
330
|
+
*/
|
|
331
|
+
export type SaveConfigInput = {
|
|
315
332
|
edits: Array<ConfigEdit>;
|
|
316
333
|
};
|
|
317
334
|
/**
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
* session's own most recent `studio:generate` produced, so it only works right after that
|
|
321
|
-
* generation and never carries file contents itself. Refused for a sandbox agent, since it holds
|
|
322
|
-
* no generated files worth packing, and refused when no prior generation exists to pack.
|
|
335
|
+
* Per-edit outcomes plus the rewritten file. `changed` is false when every edit was a no-op, so a
|
|
336
|
+
* caller can skip reloading.
|
|
323
337
|
*/
|
|
324
|
-
export type
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
338
|
+
export type SaveResult = {
|
|
339
|
+
outcomes: Array<ConfigEditOutcome>;
|
|
340
|
+
changed: boolean;
|
|
341
|
+
file?: ConfigFileView;
|
|
342
|
+
};
|
|
343
|
+
/**
|
|
344
|
+
* Asks the agent to read generated files back. Capped at {@link MAX_FILES_PER_REQUEST} paths, all
|
|
345
|
+
* of which must sit inside the output directory.
|
|
346
|
+
*/
|
|
347
|
+
export type ReadFilesInput = {
|
|
348
|
+
paths: Array<string>;
|
|
349
|
+
};
|
|
350
|
+
/**
|
|
351
|
+
* Describes the package to pack and where to PUT it. `uploadPath` is resolved against the Studio
|
|
352
|
+
* origin, so it cannot redirect the upload elsewhere.
|
|
353
|
+
*/
|
|
354
|
+
export type PublishSnapshotInput = {
|
|
355
|
+
name: string;
|
|
356
|
+
version: string;
|
|
357
|
+
bundledDependencies?: Array<string>;
|
|
358
|
+
uploadPath: string;
|
|
359
|
+
};
|
|
360
|
+
/**
|
|
361
|
+
* Identifies the uploaded snapshot. `integrity` is the subresource hash Studio verifies against.
|
|
362
|
+
*/
|
|
363
|
+
export type PublishSnapshotResult = {
|
|
364
|
+
integrity: string;
|
|
365
|
+
peerDependencies: Record<string, string>;
|
|
366
|
+
};
|
|
367
|
+
/**
|
|
368
|
+
* A generation in flight. Cap'n Web keeps the three calls pointed at the same run, so a caller can
|
|
369
|
+
* read `events()` while `result()` is still pending and `cancel()` stops it early.
|
|
370
|
+
*/
|
|
371
|
+
export type GenerationRun = {
|
|
372
|
+
events: () => Promise<ReadableStream<GenerationEvent>>;
|
|
373
|
+
result: () => Promise<GenerateResult>;
|
|
374
|
+
cancel: () => Promise<void>;
|
|
375
|
+
};
|
|
376
|
+
/**
|
|
377
|
+
* Operations Studio can invoke on an agent through a host-provided RPC transport.
|
|
378
|
+
*/
|
|
379
|
+
export type AgentApi = {
|
|
380
|
+
connect: () => Promise<ConnectMessagePayload>;
|
|
381
|
+
startGeneration: (input: GenerateInput) => GenerationRun;
|
|
382
|
+
saveConfig: (input: SaveConfigInput) => Promise<SaveResult>;
|
|
383
|
+
publishSnapshot: (input: PublishSnapshotInput) => Promise<PublishSnapshotResult>;
|
|
384
|
+
readFiles: (input: ReadFilesInput) => Promise<{
|
|
385
|
+
files: Record<string, string>;
|
|
386
|
+
}>;
|
|
387
|
+
};
|
|
388
|
+
/**
|
|
389
|
+
* Operations an agent can invoke on Studio through a host-provided RPC transport.
|
|
390
|
+
*/
|
|
391
|
+
export type StudioApi = {
|
|
392
|
+
ping: () => Promise<void>;
|
|
393
|
+
};
|
|
394
|
+
/**
|
|
395
|
+
* A live RPC session. `closed` settles when the transport drops, whichever side ended it.
|
|
396
|
+
*/
|
|
397
|
+
export type RpcConnection = {
|
|
398
|
+
studio: StudioApi;
|
|
399
|
+
closed: Promise<void>;
|
|
400
|
+
close: () => void;
|
|
337
401
|
};
|
|
338
402
|
/**
|
|
339
|
-
*
|
|
340
|
-
*
|
|
403
|
+
* Opens a transport and hands both sides their peer. Swapping this is how a test drives a session
|
|
404
|
+
* without a socket.
|
|
341
405
|
*/
|
|
342
|
-
export type
|
|
406
|
+
export type RpcConnector = (input: {
|
|
407
|
+
url: string;
|
|
408
|
+
token: string;
|
|
409
|
+
local: AgentApi;
|
|
410
|
+
}) => Promise<RpcConnection>;
|
|
343
411
|
/**
|
|
344
|
-
*
|
|
412
|
+
* How many files a single `readFiles` request may ask for at once.
|
|
345
413
|
*/
|
|
346
|
-
export declare const
|
|
347
|
-
export declare function createJobId(): string;
|
|
414
|
+
export declare const MAX_FILES_PER_REQUEST = 50;
|
|
348
415
|
/**
|
|
349
|
-
* Identifies the host running the Kubb runtime. Local to the runtime
|
|
350
|
-
*
|
|
351
|
-
* `
|
|
416
|
+
* Identifies the host running the Kubb runtime. Local to the runtime, not part of the wire: it
|
|
417
|
+
* picks which remedy a refused-permission warning names. Distinct from an agent's `type` (`user`,
|
|
418
|
+
* `cli`, `ci`, `sandbox`, `global`), which is what Studio records the agent as at pairing.
|
|
352
419
|
*/
|
|
353
420
|
export type ClientInfo = {
|
|
354
421
|
/**
|
|
355
|
-
* `cli` for
|
|
356
|
-
*
|
|
422
|
+
* `cli` for any `kubb` invocation, including `kubb studio snapshot` from CI. `docker` for the
|
|
423
|
+
* agent image.
|
|
357
424
|
*/
|
|
358
|
-
kind: 'cli' | 'docker'
|
|
425
|
+
kind: 'cli' | 'docker';
|
|
359
426
|
};
|
|
360
427
|
/**
|
|
361
|
-
*
|
|
362
|
-
*
|
|
428
|
+
* Connection payload returned by {@link AgentApi.connect}. Carries only what Studio renders, with
|
|
429
|
+
* everything about the config under one key.
|
|
363
430
|
*/
|
|
364
431
|
export type ConnectMessagePayload = {
|
|
365
432
|
/**
|
|
@@ -432,144 +499,22 @@ export type AgentPermissions = {
|
|
|
432
499
|
* `allowWrite`, which covers generated output: this one edits a hand-authored source file.
|
|
433
500
|
*/
|
|
434
501
|
allowConfigEdit: boolean;
|
|
435
|
-
};
|
|
436
|
-
/**
|
|
437
|
-
* Agent → Studio handshake. Sent when the WebSocket opens and again after a `connect` command.
|
|
438
|
-
* Carries the on-disk config baseline, granted permissions, and paths Studio needs to render the editor.
|
|
439
|
-
*/
|
|
440
|
-
export type AgentConnectMessage = {
|
|
441
|
-
type: 'agent:connect';
|
|
442
|
-
payload: ConnectMessagePayload;
|
|
443
|
-
};
|
|
444
|
-
/**
|
|
445
|
-
* Reply to a `save` command: what the agent did to the file on disk.
|
|
446
|
-
*/
|
|
447
|
-
export type AgentSaveMessage = {
|
|
448
|
-
type: 'agent:save';
|
|
449
|
-
jobId: string;
|
|
450
|
-
payload: {
|
|
451
|
-
/**
|
|
452
|
-
* Per-edit result, in the order the edits were sent.
|
|
453
|
-
*/
|
|
454
|
-
outcomes: Array<ConfigEditOutcome>;
|
|
455
|
-
/**
|
|
456
|
-
* Whether the file on disk changed. False when every edit was refused, and when the applied
|
|
457
|
-
* edits produced the text the file already had.
|
|
458
|
-
*/
|
|
459
|
-
changed: boolean;
|
|
460
|
-
/**
|
|
461
|
-
* The config file as it now stands, so Studio can re-render without a round trip. Absent when
|
|
462
|
-
* nothing was written. Named to match `config.file` in the connect payload.
|
|
463
|
-
*/
|
|
464
|
-
file?: ConfigFileView;
|
|
465
|
-
};
|
|
466
|
-
};
|
|
467
|
-
/**
|
|
468
|
-
* Reply to `studio:snapshot`. The tarball already went to storage, so this only reports whether
|
|
469
|
-
* the upload succeeded.
|
|
470
|
-
*/
|
|
471
|
-
export type AgentSnapshotMessage = {
|
|
472
|
-
type: 'agent:snapshot';
|
|
473
|
-
jobId: string;
|
|
474
|
-
payload: {
|
|
475
|
-
status: 'ok';
|
|
476
|
-
integrity: string;
|
|
477
|
-
} | {
|
|
478
|
-
status: 'error';
|
|
479
|
-
message: string;
|
|
480
|
-
};
|
|
481
|
-
};
|
|
482
|
-
/**
|
|
483
|
-
* Failure notice from Studio for something that breaks outside a generation, such as a malformed
|
|
484
|
-
* command. The agent's own failures travel as an `agent:data` message carrying a `kubb:error`
|
|
485
|
-
* payload, which keeps them ordered against the generation events around them.
|
|
486
|
-
*/
|
|
487
|
-
export type StudioErrorMessage = {
|
|
488
|
-
type: 'studio:error';
|
|
489
|
-
message: string;
|
|
490
|
-
};
|
|
491
|
-
/**
|
|
492
|
-
* Heartbeat sent by the Agent to Studio so the connection is not treated as idle.
|
|
493
|
-
*/
|
|
494
|
-
export type AgentPingMessage = {
|
|
495
|
-
type: 'agent:ping';
|
|
496
|
-
};
|
|
497
|
-
/**
|
|
498
|
-
* Studio's reply to an `agent:ping`, confirming the connection is still alive.
|
|
499
|
-
*/
|
|
500
|
-
export type StudioPongMessage = {
|
|
501
|
-
type: 'studio:pong';
|
|
502
|
-
};
|
|
503
|
-
/**
|
|
504
|
-
* Studio's acknowledgement that an `agent:connect` handshake was received and the session is
|
|
505
|
-
* fully registered: the connection now counts as available for job dispatch. Distinct from the
|
|
506
|
-
* socket merely being open, which is not yet the same thing.
|
|
507
|
-
*/
|
|
508
|
-
export type StudioReadyMessage = {
|
|
509
|
-
type: 'studio:ready';
|
|
510
|
-
};
|
|
511
|
-
/**
|
|
512
|
-
* Disconnect message sent from Studio to Agent when the session is expired or revoked.
|
|
513
|
-
* The agent should close the connection without reconnecting.
|
|
514
|
-
*/
|
|
515
|
-
export type StudioDisconnectMessage = {
|
|
516
|
-
type: 'studio:disconnect';
|
|
517
|
-
reason: 'expired' | 'revoked';
|
|
518
|
-
};
|
|
519
|
-
/**
|
|
520
|
-
* The agent going away, so Studio marks the session offline instead of waiting out the heartbeat
|
|
521
|
-
* window. The mirror of {@link StudioDisconnectMessage}.
|
|
522
|
-
*
|
|
523
|
-
* Only sent for a shutdown. An expired or revoked session was Studio's own decision, so echoing it
|
|
524
|
-
* back says nothing new.
|
|
525
|
-
*/
|
|
526
|
-
export type AgentDisconnectMessage = {
|
|
527
|
-
type: 'agent:disconnect';
|
|
528
|
-
reason: 'shutdown';
|
|
529
|
-
};
|
|
530
|
-
/**
|
|
531
|
-
* Payload of an `agent:data` message: a single Kubb generation event forwarded to Studio in real time.
|
|
532
|
-
* Generic over the hook name so `data` is typed to that hook's context tuple.
|
|
533
|
-
*/
|
|
534
|
-
export type DataMessagePayload<T extends KubbHook = KubbHook> = {
|
|
535
|
-
/**
|
|
536
|
-
* The Kubb hook this event is for (e.g. `kubb:plugin:start`).
|
|
537
|
-
*/
|
|
538
|
-
type: T;
|
|
539
502
|
/**
|
|
540
|
-
*
|
|
503
|
+
* Whether the agent hands back file source in response to `readFiles`. Always true for a
|
|
504
|
+
* sandbox agent; for a local agent it mirrors the agent's own opt-in.
|
|
541
505
|
*/
|
|
542
|
-
|
|
543
|
-
/**
|
|
544
|
-
* When the agent emitted the event, epoch milliseconds.
|
|
545
|
-
*/
|
|
546
|
-
timestamp: number;
|
|
547
|
-
/**
|
|
548
|
-
* Monotonic per-connection counter stamped in the order the agent emits events. Studio orders the
|
|
549
|
-
* event log by this, since `timestamp` has millisecond resolution and a full generation fires
|
|
550
|
-
* dozens of events per tick, and the relay can deliver them out of order.
|
|
551
|
-
*/
|
|
552
|
-
seq: number;
|
|
553
|
-
};
|
|
554
|
-
/**
|
|
555
|
-
* Envelope for a single generation event streamed from Agent to Studio. Wraps a
|
|
556
|
-
* {@link DataMessagePayload} so both sides can switch on `type: 'agent:data'`.
|
|
557
|
-
*/
|
|
558
|
-
export type DataMessage<T extends KubbHook = KubbHook> = {
|
|
559
|
-
type: 'agent:data';
|
|
560
|
-
jobId: string;
|
|
561
|
-
payload: DataMessagePayload<T>;
|
|
506
|
+
allowRead: boolean;
|
|
562
507
|
};
|
|
563
508
|
/**
|
|
564
509
|
* Response returned by the Studio `/api/agent/sessions` endpoint.
|
|
565
510
|
*/
|
|
566
511
|
export type AgentConnectResponse = {
|
|
567
512
|
/**
|
|
568
|
-
*
|
|
513
|
+
* URL the agent opens to reach the session, with the session token embedded.
|
|
569
514
|
*/
|
|
570
|
-
|
|
515
|
+
url: string;
|
|
571
516
|
/**
|
|
572
|
-
* When the session expires and the
|
|
517
|
+
* When the session expires and the url stops working (ISO 8601).
|
|
573
518
|
*/
|
|
574
519
|
expiresAt: string;
|
|
575
520
|
/**
|
|
@@ -577,7 +522,7 @@ export type AgentConnectResponse = {
|
|
|
577
522
|
*/
|
|
578
523
|
revokedAt: string | null;
|
|
579
524
|
/**
|
|
580
|
-
* Opaque session token, also embedded in `
|
|
525
|
+
* Opaque session token, also embedded in `url`. Store it to revoke the session later.
|
|
581
526
|
*/
|
|
582
527
|
sessionId: string;
|
|
583
528
|
/**
|
|
@@ -589,32 +534,11 @@ export type AgentConnectResponse = {
|
|
|
589
534
|
*/
|
|
590
535
|
isSandbox: boolean;
|
|
591
536
|
/**
|
|
592
|
-
* The Studio instance's own version.
|
|
593
|
-
*
|
|
537
|
+
* The Studio instance's own version. Returned with the RPC session so the agent can name both
|
|
538
|
+
* sides from the first connection.
|
|
594
539
|
* Absent when Studio predates the field.
|
|
595
540
|
*/
|
|
596
541
|
version?: string;
|
|
597
542
|
};
|
|
598
|
-
/**
|
|
599
|
-
* Every message that can cross the agent WebSocket, in either direction. Narrow it with the
|
|
600
|
-
* `is*Message` guards below before reading a variant's fields.
|
|
601
|
-
*/
|
|
602
|
-
export type AgentMessage = CommandMessage | DataMessage | AgentConnectMessage | AgentSaveMessage | AgentSnapshotMessage | AgentPingMessage | AgentDisconnectMessage | StudioErrorMessage | StudioPongMessage | StudioReadyMessage | StudioDisconnectMessage;
|
|
603
|
-
export declare function isCommandMessage(msg: AgentMessage): msg is CommandMessage;
|
|
604
|
-
/**
|
|
605
|
-
* Type guard to narrow a data message to a specific event type.
|
|
606
|
-
*
|
|
607
|
-
* @example
|
|
608
|
-
* ```ts
|
|
609
|
-
* if (isDataMessage(msg, 'kubb:plugin:start')) {
|
|
610
|
-
* // msg.payload.data is now typed as [ctx: { plugin: { name: string } }]
|
|
611
|
-
* const pluginName = msg.payload.data[0].plugin.name
|
|
612
|
-
* }
|
|
613
|
-
* ```
|
|
614
|
-
*/
|
|
615
|
-
export declare function isDataMessage<T extends KubbHook>(msg: AgentMessage, type?: T): msg is DataMessage<T>;
|
|
616
|
-
export declare function isStudioPongMessage(msg: AgentMessage): msg is StudioPongMessage;
|
|
617
|
-
export declare function isStudioReadyMessage(msg: AgentMessage): msg is StudioReadyMessage;
|
|
618
|
-
export declare function isDisconnectMessage(msg: AgentMessage): msg is StudioDisconnectMessage;
|
|
619
543
|
//#endregion
|
|
620
544
|
//# sourceMappingURL=protocol.d.ts.map
|