diff --git a/.github/scripts/.gitignore b/.github/scripts/.gitignore
new file mode 100644
index 0000000..a14702c
--- /dev/null
+++ b/.github/scripts/.gitignore
@@ -0,0 +1,34 @@
+# dependencies (bun install)
+node_modules
+
+# output
+out
+dist
+*.tgz
+
+# code coverage
+coverage
+*.lcov
+
+# logs
+logs
+_.log
+report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
+
+# dotenv environment variable files
+.env
+.env.development.local
+.env.test.local
+.env.production.local
+.env.local
+
+# caches
+.eslintcache
+.cache
+*.tsbuildinfo
+
+# IntelliJ based IDEs
+.idea
+
+# Finder (MacOS) folder config
+.DS_Store
diff --git a/.github/scripts/bun.lock b/.github/scripts/bun.lock
new file mode 100644
index 0000000..cc1bb9d
--- /dev/null
+++ b/.github/scripts/bun.lock
@@ -0,0 +1,39 @@
+{
+ "lockfileVersion": 1,
+ "workspaces": {
+ "": {
+ "name": "scripts",
+ "dependencies": {
+ "@types/js-yaml": "^4.0.9",
+ "js-yaml": "^4.1.0",
+ },
+ "devDependencies": {
+ "@types/bun": "latest",
+ },
+ "peerDependencies": {
+ "typescript": "^5",
+ },
+ },
+ },
+ "packages": {
+ "@types/bun": ["@types/bun@1.2.20", "", { "dependencies": { "bun-types": "1.2.20" } }, "sha512-dX3RGzQ8+KgmMw7CsW4xT5ITBSCrSbfHc36SNT31EOUg/LA9JWq0VDdEXDRSe1InVWpd2yLUM1FUF/kEOyTzYA=="],
+
+ "@types/js-yaml": ["@types/js-yaml@4.0.9", "", {}, "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg=="],
+
+ "@types/node": ["@types/node@24.3.0", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="],
+
+ "@types/react": ["@types/react@19.1.10", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg=="],
+
+ "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
+
+ "bun-types": ["bun-types@1.2.20", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-pxTnQYOrKvdOwyiyd/7sMt9yFOenN004Y6O4lCcCUoKVej48FS5cvTw9geRaEcB9TsDZaJKAxPTVvi8tFsVuXA=="],
+
+ "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
+
+ "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
+
+ "typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
+
+ "undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="],
+ }
+}
diff --git a/.github/scripts/code_samples.config.json b/.github/scripts/code_samples.config.json
new file mode 100644
index 0000000..f168e7f
--- /dev/null
+++ b/.github/scripts/code_samples.config.json
@@ -0,0 +1,11 @@
+{
+ "variants": {
+ "post /invocations": [
+ { "name": "async", "overrides": { "async": true } }
+ ],
+ "post /browsers": [
+ { "name": "timeout", "overrides": { "timeout_seconds": 300 } },
+ { "name": "live-view", "overrides": { "log": "browser.browser_live_view_url" } }
+ ]
+ }
+}
diff --git a/.github/scripts/generate_code_samples.ts b/.github/scripts/generate_code_samples.ts
new file mode 100644
index 0000000..2e6188d
--- /dev/null
+++ b/.github/scripts/generate_code_samples.ts
@@ -0,0 +1,515 @@
+#!/usr/bin/env bun
+
+import { readdir, readFile, writeFile, mkdir, unlink } from "fs/promises";
+import path from "path";
+import yaml from "js-yaml"; // for YAML support
+
+const OPENAPI_URL =
+ "https://app.stainless.com/api/spec/documented/kernel/openapi.documented.yml";
+
+const TARGET_DIRS = ["browsers", "apps"]; // adjust as needed
+const SNIPPETS_ROOT = path.resolve("snippets/openapi");
+
+// Only emit code samples for these languages (normalized): e.g., ["typescript", "python"]
+const TARGET_LANGUAGES = ["typescript", "python"] as const;
+type SupportedLanguage =
+ | (typeof TARGET_LANGUAGES)[number]
+ | "javascript"
+ | "go";
+
+function normalizeLang(input: string): SupportedLanguage {
+ const lc = (input || "").toLowerCase();
+ if (lc === "ts" || lc === "typescript") return "typescript";
+ if (lc === "py" || lc === "python") return "python";
+ if (
+ lc === "js" ||
+ lc === "javascript" ||
+ lc === "node" ||
+ lc === "node.js" ||
+ lc === "nodejs"
+ )
+ return "javascript";
+ if (lc === "go" || lc === "golang") return "go";
+ return lc as SupportedLanguage;
+}
+
+function renderFenceInfo(lang: SupportedLanguage, rawLang: string): string {
+ if (lang === "typescript" || lang === "javascript")
+ return "typescript Typescript/Javascript";
+ if (lang === "python") return "python Python";
+ return `${lang} ${toTitleCase(rawLang)}`;
+}
+
+function renderValueForLanguage(
+ lang: SupportedLanguage,
+ value: unknown
+): string {
+ const type = typeof value;
+ if (type === "boolean") {
+ const b = value as boolean;
+ return lang === "python" ? (b ? "True" : "False") : b ? "true" : "false";
+ }
+ if (type === "number") return String(value);
+ if (type === "string") {
+ // Use double quotes across both for simplicity
+ return JSON.stringify(value);
+ }
+ // Fallback to JSON for objects/arrays; Python will be JSON-like which is acceptable for docs
+ try {
+ return JSON.stringify(value);
+ } catch {
+ return String(value);
+ }
+}
+
+function injectParamsIntoObjectLiteral(
+ lang: SupportedLanguage,
+ objectLiteral: string,
+ params: Record
+): string {
+ let updated = objectLiteral;
+ for (const [key, val] of Object.entries(params)) {
+ const valueStr = renderValueForLanguage(lang, val);
+ const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
+ // Try to replace existing key first (handles both quoted and unquoted keys)
+ const keyPatterns = [
+ new RegExp(`(([,\{\n\r\t\s])${escapedKey}\\s*:\\s*)([^,\n}]+)`, "m"), // JS/TS style key: value
+ new RegExp(`(([,\{\n\r\t\s])\"${escapedKey}\"\\s*:\\s*)([^,\n}]+)`, "m"), // JSON/Python style "key": value
+ ];
+ let replaced = false;
+ for (const pattern of keyPatterns) {
+ if (pattern.test(updated)) {
+ updated = updated.replace(pattern, `$1${valueStr}`);
+ replaced = true;
+ break;
+ }
+ }
+ if (!replaced) {
+ // Insert after the first opening brace
+ if (lang === "python") {
+ const insertion = `"${key}": ${valueStr}, `;
+ updated = updated.replace(/\{\s*/, (m) => m + insertion);
+ } else {
+ const isMultiline = /\{\s*\n/.test(updated);
+ const indentMatch = updated.match(/\{\s*\n([ \t]*)/);
+ const indent = indentMatch?.[1] ?? "";
+ const insertion = isMultiline
+ ? `${indent}${key}: ${valueStr},\n${indent}`
+ : `${key}: ${valueStr}, `;
+ if (isMultiline) {
+ // Normalize to a single newline after '{' then insert our line
+ updated = updated.replace(/\{\s*/, "{\n");
+ updated = updated.replace(/\{\n/, `{\n${insertion}`);
+ } else {
+ updated = updated.replace(/\{\s*/, (m) => m + insertion);
+ }
+ }
+ }
+ }
+ // For JS/TS single-line objects, remove any trailing comma before the closing brace
+ if (lang !== "python") {
+ updated = updated.replace(/,\s*}/, " }");
+ }
+ return updated;
+}
+
+function injectParamsIntoPythonArgs(
+ argList: string,
+ params: Record
+): string {
+ let updated = argList;
+ // Ensure trailing comma handling is clean; if arguments are on multiple lines, add newline before inserted kwarg
+ for (const [key, val] of Object.entries(params)) {
+ const valueStr = renderValueForLanguage("python", val);
+ const pattern = new RegExp(`(\\b${key}\\s*=)\\s*([^,\n)]+)`, "m");
+ if (pattern.test(updated)) {
+ updated = updated.replace(pattern, `$1 ${valueStr}`);
+ } else {
+ const trimmed = updated.trim();
+ if (trimmed.length === 0) {
+ updated = `${key}=${valueStr}`;
+ } else {
+ const isMultiline = /\n/.test(updated);
+ const hasTrailingComma = /,\s*$/.test(updated);
+ if (isMultiline) {
+ const firstArgIndentMatch = updated.match(/^([ \t]+)[A-Za-z_]/m);
+ const lastLineIndentMatch = updated.match(/\n([ \t]*)[^\n]*$/);
+ const indent =
+ firstArgIndentMatch?.[1] ?? lastLineIndentMatch?.[1] ?? " ";
+ const prefix = updated.replace(/\s*$/, "");
+ const commaPrefix = hasTrailingComma ? prefix : `${prefix},`;
+ updated = `${commaPrefix}\n${indent}${key}=${valueStr},`;
+ } else {
+ const sep = hasTrailingComma ? " " : ", ";
+ updated = `${updated.replace(/\s*$/, "")}${sep}${key}=${valueStr}`;
+ }
+ }
+ }
+ }
+ return updated;
+}
+
+function applyOverridesToSource(
+ lang: SupportedLanguage,
+ src: string,
+ overrides?: Record
+): string {
+ if (!overrides || Object.keys(overrides).length === 0) return src;
+ let transformed = src;
+
+ // Heuristic: find the first object literal in a X.create(...) call and inject params (excluding special keys like 'log')
+ const { log: _logOverride, ...injectionOverrides } = overrides as Record<
+ string,
+ unknown
+ >;
+ if (Object.keys(injectionOverrides).length > 0) {
+ const createCall = /(\b\w+\.(create|new)\()([\s\S]*?)(\))/m;
+ const match = transformed.match(createCall);
+ if (match) {
+ const before = transformed.slice(0, match.index ?? 0);
+ const after = transformed.slice((match.index ?? 0) + match[0].length);
+ const inside = match[3] ?? "";
+ const objectMatch = inside.match(/\{[\s\S]*?\}/m);
+ if (objectMatch) {
+ const objBefore = inside.slice(0, objectMatch.index!);
+ const obj = objectMatch[0];
+ const objAfter = inside.slice(
+ (objectMatch.index || 0) + objectMatch[0].length
+ );
+ const injected = injectParamsIntoObjectLiteral(
+ lang,
+ obj,
+ injectionOverrides
+ );
+ const newInside = objBefore + injected + objAfter;
+ transformed = before + match[1] + newInside + match[4] + after;
+ } else if (lang === "python") {
+ const injectedArgs = injectParamsIntoPythonArgs(
+ inside,
+ injectionOverrides
+ );
+ const needsNewlineBeforeParen =
+ /\n/.test(injectedArgs) && !/\n\s*$/.test(injectedArgs);
+ const joiner = needsNewlineBeforeParen ? "\n" : "";
+ transformed =
+ before + match[1] + injectedArgs + joiner + match[4] + after;
+ } else {
+ // JS/TS with no object literal: insert one and inject
+ const injectedObj = injectParamsIntoObjectLiteral(
+ lang,
+ "{}",
+ injectionOverrides
+ );
+ transformed = before + match[1] + injectedObj + match[4] + after;
+ }
+ }
+ }
+
+ // Apply log override if present: replace argument of the last console.log/print call
+ const logExpr = (overrides as Record)?.log;
+ if (typeof logExpr === "string" && logExpr.trim().length > 0) {
+ if (lang === "python") {
+ const regex = /print\(([^\)]*)\)/g;
+ let lastMatch: RegExpExecArray | null = null;
+ let m: RegExpExecArray | null;
+ while ((m = regex.exec(transformed)) !== null) lastMatch = m;
+ if (lastMatch) {
+ const start = lastMatch.index;
+ const end = start + lastMatch[0].length;
+ transformed =
+ transformed.slice(0, start) +
+ `print(${logExpr})` +
+ transformed.slice(end);
+ }
+ } else {
+ const regex = /console\.log\(([^\)]*)\)/g;
+ let lastMatch: RegExpExecArray | null = null;
+ let m: RegExpExecArray | null;
+ while ((m = regex.exec(transformed)) !== null) lastMatch = m;
+ if (lastMatch) {
+ const start = lastMatch.index;
+ const end = start + lastMatch[0].length;
+ transformed =
+ transformed.slice(0, start) +
+ `console.log(${logExpr})` +
+ transformed.slice(end);
+ }
+ }
+ }
+
+ return transformed;
+}
+
+function parseOverridesString(
+ raw: string
+): Record | undefined {
+ // Rebuild as JSON object string; heuristically quote keys
+ let jsonish = `{ ${raw} }`;
+ // Quote unquoted keys (simple heuristic)
+ jsonish = jsonish.replace(/([,{\s])([A-Za-z_][A-Za-z0-9_]*)\s*:/g, '$1"$2":');
+ // Normalize single quotes to double quotes
+ jsonish = jsonish.replace(/'([^']*)'/g, '"$1"');
+ try {
+ return JSON.parse(jsonish);
+ } catch {
+ return undefined;
+ }
+}
+
+function extractOverridesFromAttributes(
+ attrs: string | undefined
+): Record | undefined {
+ if (!attrs) return undefined;
+ const idx = attrs.indexOf("overrides=");
+ if (idx === -1) return undefined;
+ // Find first '{' after 'overrides='
+ const start = attrs.indexOf("{", idx);
+ if (start === -1) return undefined;
+ let depth = 0;
+ let end = -1;
+ for (let i = start; i < attrs.length; i++) {
+ const ch = attrs[i];
+ if (ch === "{") depth++;
+ else if (ch === "}") {
+ depth--;
+ if (depth === 0) {
+ end = i;
+ break;
+ }
+ }
+ }
+ if (end === -1) return undefined;
+ const raw = attrs.slice(start, end + 1);
+ return parseOverridesString(raw);
+}
+
+function toTitleCase(input: string): string {
+ if (!input) return "";
+ return input.charAt(0).toUpperCase() + input.slice(1).toLowerCase();
+}
+
+type VariantConfig = { name: string; overrides?: Record };
+type CodeSamplesConfig = {
+ variants?: Record; // key: "method /path" lowercased
+};
+
+async function readConfig(): Promise {
+ const configPath = path.resolve(".github/scripts/code_samples.config.json");
+ try {
+ const text = await readFile(configPath, "utf8");
+ return JSON.parse(text) as CodeSamplesConfig;
+ } catch {
+ return {};
+ }
+}
+
+async function fetchOpenAPISpec() {
+ console.log(`📥 Fetching OpenAPI spec from ${OPENAPI_URL}`);
+ const res = await fetch(OPENAPI_URL!);
+ if (!res.ok)
+ throw new Error(`Failed to fetch OpenAPI spec: ${res.statusText}`);
+ const text = await res.text();
+
+ // Try JSON first, fallback to YAML
+ try {
+ return JSON.parse(text);
+ } catch {
+ return yaml.load(text);
+ }
+}
+
+async function getMdxFiles(dir: string): Promise {
+ const entries = await readdir(dir, { withFileTypes: true });
+ const files: string[] = [];
+ for (const entry of entries) {
+ const fullPath = path.join(dir, entry.name);
+ if (entry.isDirectory()) {
+ files.push(...(await getMdxFiles(fullPath)));
+ } else if (entry.isFile() && entry.name.endsWith(".mdx")) {
+ files.push(fullPath);
+ }
+ }
+ return files;
+}
+
+function extractCodeSamples(
+ spec: any,
+ endpoint: string,
+ method?: string,
+ overrides?: Record
+): string {
+ const pathItem = spec.paths?.[endpoint];
+ if (!pathItem) return `⚠️ No spec found for ${endpoint}`;
+
+ const blocks: string[] = [];
+
+ const methods = method ? [method.toLowerCase()] : Object.keys(pathItem); // all methods if not specified
+
+ for (const m of methods) {
+ const op = pathItem[m];
+ if (op?.["x-codeSamples"]) {
+ for (const sample of op["x-codeSamples"]) {
+ const rawLang = typeof sample.lang === "string" ? sample.lang : "";
+ const normalized = normalizeLang(rawLang);
+ const allowedNormalized = [
+ "typescript",
+ "javascript",
+ "python",
+ ] as const;
+ if (!(allowedNormalized as readonly string[]).includes(normalized))
+ continue;
+ const displayLang: "typescript" | "python" =
+ normalized === "python" ? "python" : "typescript";
+
+ const fenceInfo = renderFenceInfo(displayLang, rawLang);
+ const transformedSource = applyOverridesToSource(
+ displayLang,
+ String(sample.source ?? "").trim(),
+ overrides
+ );
+ blocks.push(`\n\`\`\`${fenceInfo}\n${transformedSource}\n\`\`\`\n`);
+ }
+ }
+ }
+
+ return blocks.length > 0
+ ? blocks.join("\n")
+ : `⚠️ No code samples for ${
+ method ? method.toUpperCase() : "any"
+ } ${endpoint}`;
+}
+
+function slugifyEndpoint(method: string, endpoint: string): string {
+ const m = (method || "").toLowerCase().replace(/[^a-z]/g, "");
+ let ep = endpoint.replace(/^\//, "");
+ // Unwrap path params like {id} -> id
+ ep = ep.replace(/\{([^}]+)\}/g, "$1");
+ // Sanitize and normalize
+ ep = ep
+ .replace(/[^a-zA-Z0-9\-/_]/g, "-")
+ .replace(/[\/]/g, "-")
+ .replace(/--+/g, "-")
+ .replace(/^-+|-+$/g, "");
+ return `${m}-${ep}`.toLowerCase();
+}
+
+async function writeSnippetFile(filePath: string, blocks: string) {
+ const dir = path.dirname(filePath);
+ await mkdir(dir, { recursive: true });
+ const content = `\n${blocks.trim()}\n\n`;
+ await writeFile(filePath, content, "utf8");
+}
+
+async function generateSnippets(spec: any) {
+ const config = await readConfig();
+ const paths = spec.paths || {};
+ for (const endpoint of Object.keys(paths)) {
+ const pathItem = paths[endpoint];
+ for (const method of Object.keys(pathItem)) {
+ const key = `${method.toLowerCase()} ${endpoint}`.toLowerCase();
+ const baseBlocks = extractCodeSamples(spec, endpoint, method).trim();
+ if (!baseBlocks.startsWith("⚠️ ")) {
+ const baseSlug = slugifyEndpoint(method, endpoint);
+ const baseFile = path.join(SNIPPETS_ROOT, `${baseSlug}.mdx`);
+ await writeSnippetFile(baseFile, baseBlocks);
+ console.log(
+ `🧩 Wrote snippet: ${path.relative(process.cwd(), baseFile)}`
+ );
+ }
+ const variants = config.variants?.[key] || [];
+ for (const variant of variants) {
+ const vBlocks = extractCodeSamples(
+ spec,
+ endpoint,
+ method,
+ variant.overrides
+ ).trim();
+ if (!vBlocks.startsWith("⚠️ ")) {
+ const vSlug = `${slugifyEndpoint(method, endpoint)}-${variant.name}`;
+ const vFile = path.join(SNIPPETS_ROOT, `${vSlug}.mdx`);
+ await writeSnippetFile(vFile, vBlocks);
+ console.log(
+ `🧩 Wrote snippet: ${path.relative(process.cwd(), vFile)}`
+ );
+ }
+ }
+ }
+ }
+}
+
+async function cleanupOldSnippets() {
+ try {
+ const entries = await readdir(SNIPPETS_ROOT, { withFileTypes: true });
+ const stale = entries
+ .filter((e) => e.isFile())
+ .map((e) => e.name)
+ .filter((name) => name.endsWith("-.mdx"));
+ for (const name of stale) {
+ const full = path.join(SNIPPETS_ROOT, name);
+ await unlink(full);
+ console.log(
+ `🧹 Removed stale snippet: ${path.relative(process.cwd(), full)}`
+ );
+ }
+ } catch {
+ // ignore if folder doesn't exist yet
+ }
+}
+
+async function processFile(file: string, spec: any) {
+ let content = await readFile(file, "utf8");
+
+ // Matches get /path
+ // or /path
+ const tagRegex =
+ /]*)>\s*(?:(get|post|put|delete|patch|options|head)\s+)?(\/[^\s<]+)(?:\s+(\{[\s\S]*?\}))?\s*<\/OpenAPICodeGroup>/gi;
+
+ let changed = false;
+ content = content.replace(
+ tagRegex,
+ (_, attrs, method, endpoint, jsonOverrides) => {
+ changed = true;
+ let overrides: Record | undefined = undefined;
+ const attrOverrides = extractOverridesFromAttributes(attrs);
+ if (attrOverrides) overrides = { ...attrOverrides };
+ if (jsonOverrides) {
+ const inline = parseOverridesString(jsonOverrides);
+ if (inline) overrides = { ...(overrides || {}), ...inline };
+ }
+ const blocks = extractCodeSamples(
+ spec,
+ endpoint,
+ method,
+ overrides
+ ).trim();
+ return `\n${blocks}\n`;
+ }
+ );
+
+ if (changed) {
+ console.log(`✏️ Updating ${file}`);
+ await writeFile(file, content, "utf8");
+ }
+}
+
+async function main() {
+ const spec = await fetchOpenAPISpec();
+
+ // Always generate snippet files from OpenAPI
+ await cleanupOldSnippets();
+ await generateSnippets(spec);
+
+ for (const dir of TARGET_DIRS) {
+ const files = await getMdxFiles(dir);
+ for (const file of files) {
+ await processFile(file, spec);
+ }
+ }
+
+ console.log("✅ Done updating docs");
+}
+
+main().catch((err) => {
+ console.error(err);
+ process.exit(1);
+});
diff --git a/.github/scripts/package.json b/.github/scripts/package.json
new file mode 100644
index 0000000..769e60e
--- /dev/null
+++ b/.github/scripts/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "scripts",
+ "private": true,
+ "devDependencies": {
+ "@types/bun": "latest"
+ },
+ "peerDependencies": {
+ "typescript": "^5"
+ },
+ "dependencies": {
+ "@types/js-yaml": "^4.0.9",
+ "js-yaml": "^4.1.0"
+ }
+}
diff --git a/.github/scripts/tsconfig.json b/.github/scripts/tsconfig.json
new file mode 100644
index 0000000..bfa0fea
--- /dev/null
+++ b/.github/scripts/tsconfig.json
@@ -0,0 +1,29 @@
+{
+ "compilerOptions": {
+ // Environment setup & latest features
+ "lib": ["ESNext"],
+ "target": "ESNext",
+ "module": "Preserve",
+ "moduleDetection": "force",
+ "jsx": "react-jsx",
+ "allowJs": true,
+
+ // Bundler mode
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "noEmit": true,
+
+ // Best practices
+ "strict": true,
+ "skipLibCheck": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedIndexedAccess": true,
+ "noImplicitOverride": true,
+
+ // Some stricter flags (disabled by default)
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
+ "noPropertyAccessFromIndexSignature": false
+ }
+}
diff --git a/.github/workflows/generate_code_snippets.yaml b/.github/workflows/generate_code_snippets.yaml
new file mode 100644
index 0000000..753d74a
--- /dev/null
+++ b/.github/workflows/generate_code_snippets.yaml
@@ -0,0 +1,34 @@
+name: Update Docs with Code Samples from OpenAPI
+
+on:
+ push:
+ branches-ignore:
+ - main
+
+permissions:
+ contents: write
+
+jobs:
+ update-docs:
+ runs-on: ubuntu-latest
+ if: github.actor != 'github-actions[bot]' # prevent infinite loop
+ steps:
+ - name: Checkout branch
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ github.ref_name }}
+ fetch-depth: 0
+
+ - name: Install Bun
+ uses: oven-sh/setup-bun@v1
+
+ - name: Run update script
+ run: bun run .github/scripts/generate_code_samples.ts
+
+ - name: Commit changes
+ run: |
+ git config --global user.name "github-actions[bot]"
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+ git add .
+ git commit -m "docs: update code samples from OpenAPI" || echo "No changes"
+ git push origin "HEAD:${{ github.ref }}"
diff --git a/README.md b/README.md
index ac7552c..c9dd00c 100644
--- a/README.md
+++ b/README.md
@@ -7,13 +7,48 @@
-
This is the documentation for the Kernel platform. It's connected to [docs.onkernel.com](https://docs.onkernel.com).
+## Code Snippets
+
+Code samples in the docs are generated from our OpenAPI spec so the examples stay in sync with the API. There are two ways the generator is invoked:
+
+- Custom MDX tag: use `get /api/v1/users` (or omit the verb to use the default behavior).
+
+How the generator works (current behavior):
+
+- The script is `.github/scripts/generate_code_samples.ts` and is executed with Bun. It fetches the OpenAPI spec from the URL configured at the top of that script.
+- It reads `x-codeSamples` entries for each operation and extracts samples for TypeScript/JavaScript and Python. Samples are normalized and may be transformed by simple "overrides" (see the script for the override parsing and injection heuristics).
+- It writes snippet files under `snippets/openapi/` as MDX files containing a `` with the generated code fences. It also updates any MDX files under `apps/` and `browsers/` that contain the inline or tag forms by replacing them with the generated `` blocks.
+- The generator can produce a base snippet and additional variant snippets controlled by `.github/scripts/code_samples.config.json` (variants are keyed by `"method /path"`).
+- The script also removes stale snippet files matching the `-.mdx` suffix pattern.
+
+How it affects the repository:
+
+- New or updated files are created under `snippets/openapi/*.mdx`.
+- MDX pages in `apps/` and `browsers/` may be modified in-place to replace ``/mustache tags with generated `` blocks.
+- A GitHub Action (`.github/workflows/generate_code_snippets.yaml`) runs the script on push (except to `main`), commits any changes, and pushes them to the `gh_action_generated_docs` branch so Mintlify can deploy the generated docs.
+
+Notes and gotchas:
+
+- The generator runs remotely against the OpenAPI URL defined in the script, so it needs network access and the spec to include `x-codeSamples` for useful output.
+- The override parsing and code injection are heuristic — complex sample sources might not be transformed exactly as intended. See the script for details on how keys/`log` overrides are applied.
+- The GitHub Action installs Bun and runs the script; if you run it locally, install Bun and run `bun run .github/scripts/generate_code_samples.ts` from the repo root.
+
+Example: to add a snippet placeholder to a page, add `get /api/v1/users` and let the generator fill `snippets/openapi` and update the page during the next run.
+
+## Local Development
+
+To run the docs locally, you can use the following command:
+
+```bash
+mintlify dev
+```
+
## Contributing
We welcome contributions to the documentation. Please feel free to submit a pull request with your changes. See [CONTRIBUTING.md](CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) for more details.
## License
-This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details.
\ No newline at end of file
+This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details.
diff --git a/apps/invoke.mdx b/apps/invoke.mdx
index 5810ca6..16cca48 100644
--- a/apps/invoke.mdx
+++ b/apps/invoke.mdx
@@ -2,90 +2,22 @@
title: "Invoking"
---
+import SynchronousInvocation from '/snippets/openapi/post-invocations.mdx';
+import AsyncInvocation from '/snippets/openapi/post-invocations-async.mdx';
+
## Via API
-You can invoke your app by making a `POST` request to Kernel's API. **For automations and agents that take longer than 100 seconds, use [async invocations](/apps/invoke#asynchronous-invocations).**
-
-
- Synchronous invocations time out after 100 seconds.
-
-
-
-```typescript Typescript/Javascript
-import { Kernel } from '@onkernel/sdk';
-
-const {
- id,
- status,
- status_reason,
- output
-} = await Kernel.invocations.create({
- app_name: app_name,
- action_name: action_name,
- version: "latest"
-});
-```
+You can invoke your app by making a `POST` request to Kernel's API. **For automations and agents that take longer than 100 seconds, use [async invocations](/apps/invoke#asynchronous-invocations).**
-```python Python
-import kernel
-
-result = kernel.invocations.create({
- "app_name": app_name,
- "action_name": action_name,
- "version": "latest"
-})
-invocation_id, status, status_reason, output = (
- result["id"],
- result["status"],
- result["status_reason"],
- result["output"]
-)
- result["id"],
- result["status"],
- result["status_reason"],
- result["output"]
-)
-```
+Synchronous invocations time out after 100 seconds.
-
+
### Asynchronous invocations
-For long running jobs, use asynchronous invocations to trigger Kernel actions without waiting for the result. You can then poll its [status](/apps/status) for the result.
-
-
-```typescript Typescript/Javascript
-import { Kernel } from '@onkernel/sdk';
-
-const {
- id,
- status, // status will be QUEUED
-} = await Kernel.invocations.create({
- app_name: app_name,
- action_name: action_name,
- version: "latest",
- async: true,
-});
-```
-```python Python
-import kernel
-
-result = kernel.invocations.create({
- "app_name": app_name,
- "action_name": action_name,
- "version": "latest",
- "async": True
-})
-invocation_id, status = (
- result["id"],
- result["status"] # status will be QUEUED
-)
- result["id"],
- result["status"] # status will be QUEUED
-)
-```
+For long running jobs, use asynchronous invocations to trigger Kernel actions without waiting for the result. You can then poll its [status](/apps/status) for the result.
-
+
## Via CLI
@@ -96,9 +28,11 @@ kernel invoke
```
### Payload parameter
+
`--payload` allows you to invoke the action with specified parameters. This enables your action to receive and handle dynamic inputs at runtime. For example:
+
```bash
-kernel invoke
+kernel invoke
--payload '{"tshirt_size": "small", "color": "black", "shipping_address": "2 Mint Plz, San Francisco CA 94103"}'
```
diff --git a/apps/status.mdx b/apps/status.mdx
index 8fcbf2f..6b3c717 100644
--- a/apps/status.mdx
+++ b/apps/status.mdx
@@ -2,41 +2,11 @@
title: "Status"
---
-Once you've [deployed](/apps/deploy) an app and invoked it, you can check its status.
-
-
-```typescript Typescript/Javascript
-import { Kernel } from '@onkernel/sdk';
-
-const {
- id,
- app_name,
- action_name,
- payload,
- output,
- started_at,
- finished_at,
- status,
- status_reason
-} = await Kernel.invocations.get(invocation_id);
-```
+import GetInvocationStatus from '/snippets/openapi/get-invocations-id.mdx';
-```python Python
-import kernel
-
-data = kernel.invocations.get(invocation_id)
-invocation_id = data["id"]
-app_name = data["app_name"]
-action_name = data["action_name"]
-payload = data["payload"]
-output = data["output"]
-started_at = data["started_at"]
-finished_at = data["finished_at"]
-status = data["status"]
-status_reason = data["status_reason"]
-```
+Once you've [deployed](/apps/deploy) an app and invoked it, you can check its status.
-
+
An invocation ends once its code execution finishes.
diff --git a/apps/stop.mdx b/apps/stop.mdx
index 5bc6132..4164b3c 100644
--- a/apps/stop.mdx
+++ b/apps/stop.mdx
@@ -2,6 +2,8 @@
title: "Stopping"
---
+import StopInvocation from '/snippets/openapi/patch-invocations-id.mdx';
+
You can terminate an invocation that's running. You can use this to stop automations or agents stuck in an infinite loop.
@@ -9,22 +11,9 @@ Terminating an invocation also destroys any browsers associated with it.
## Via API
-You can also do this by making a `DELETE` request to Kernel's API:
-
-
-```typescript Typescript/Javascript
-import { Kernel } from '@onkernel/sdk';
-
-await Kernel.invocations.delete(invocationId);
-```
-
-```python Python
-import kernel
-
-kernel.invocations.delete(invocation_id)
-```
-
+You can also do this via the API:
+
## Via CLI
Use `ctrl-c` in the terminal tab where you launched the invocation.
diff --git a/browsers/create-a-browser.mdx b/browsers/create-a-browser.mdx
index e68269c..832e735 100644
--- a/browsers/create-a-browser.mdx
+++ b/browsers/create-a-browser.mdx
@@ -2,26 +2,16 @@
title: "Create a Browser"
---
+import CreateBrowserSnippet from '/snippets/openapi/post-browsers.mdx';
+import DeleteBrowserSnippet from '/snippets/openapi/delete-browsers-id.mdx';
+
Kernel browsers were designed to be lightweight, fast, and efficient for cloud-based browser automations at scale. They can be used as part of the Kernel [app platform](/apps/develop) or connected to from another service with the Chrome DevTools Protocol.
## 1. Create a Kernel browser
Use our SDK to create a browser:
-
-```typescript Typescript/Javascript
-import { Kernel } from '@onkernel/sdk';
-const kernel = new Kernel();
-const kernelBrowser = await kernel.browsers.create();
-```
-
-```Python Python
-import kernel
-client = Kernel()
-kernel_browser = client.browsers.create()
-```
-
-
+
## 2. Connect to the browser with the Chrome DevTools Protocol
@@ -50,16 +40,7 @@ browser = playwright.chromium.connect_over_cdp(kernel_browser.cdp_ws_url)
When you're finished with the browser, you can delete it:
-
-```typescript Typescript/Javascript
-await client.browsers.deleteByID(kernel_browser.session_id);
-```
-
-```Python Python
-client.browsers.delete_by_id(kernel_browser.session_id)
-```
-
-
+
You can also delete the browser by [specifiying a timeout](/browsers/termination#timeouts-for-non-persisted-browsers) when you create the browser.
## Full example
diff --git a/browsers/live-view.mdx b/browsers/live-view.mdx
index 0c2cbc8..970825d 100644
--- a/browsers/live-view.mdx
+++ b/browsers/live-view.mdx
@@ -2,26 +2,13 @@
title: "Live View"
---
+import CreateBrowserForLiveView from '/snippets/openapi/post-browsers-live-view.mdx';
+
Humans-in-the-loop can access the live view of Kernel browsers in real-time to resolve errors or take unscripted actions.
To access the live view, visit the `browser_live_view_url` provided when you create a Kernel browser:
-
-```typescript Typescript/Javascript
-import { Kernel } from '@onkernel/sdk';
-const kernel = new Kernel();
-const kernelBrowser = await kernel.browsers.create();
-console.log(kernelBrowser.browser_live_view_url);
-```
-
-```python Python
-import kernel
-client = Kernel()
-kernel_browser = client.browsers.create()
-print(kernel_browser.browser_live_view_url)
-```
-
-
+
## Query parameters
diff --git a/browsers/termination.mdx b/browsers/termination.mdx
index ca4062a..9798e67 100644
--- a/browsers/termination.mdx
+++ b/browsers/termination.mdx
@@ -2,37 +2,16 @@
title: "Termination & Timeouts"
---
+import DeleteBrowser from '/snippets/openapi/delete-browsers-id.mdx';
+import CreateBrowserTimeout from '/snippets/openapi/post-browsers-timeout.mdx';
+
Kernel browsers should be terminated after you're done with them.
## Via API
You can delete a browser by making a `DELETE` request to Kernel's API:
-
-```typescript Typescript/Javascript
-import Kernel from '@onkernel/sdk';
-const kernel = new Kernel();
-
-// Delete a persisted browser
-await kernel.browsers.delete({ persistent_id: 'persistent_id' });
-
-// Delete a non-persisted browser
-await kernel.browsers.deleteByID('htzv5orfit78e1m2biiifpbv');
-```
-
-```python Python
-from kernel import Kernel
-client = Kernel()
-
-# Delete a persisted browser
-client.browsers.delete(
- persistent_id="persistent_id",
-)
-
-# Delete a non-persisted browser
-client.browsers.delete_by_id('htzv5orfit78e1m2biiifpbv')
-```
-
+
## Timeouts for non-persisted browsers
@@ -40,16 +19,4 @@ If you don't manually delete a `non-persisted` browser, deletion will happen aut
You can set a custom timeout of up to 24 hours via the API:
-
-```typescript Typescript/Javascript
-import { Kernel } from '@onkernel/sdk';
-const kernel = new Kernel();
-const kernelBrowser = await kernel.browsers.create({ timeout_seconds: 300 });
-```
-
-```python Python
-import kernel
-client = Kernel()
-kernel_browser = client.browsers.create(timeout_seconds=300)
-```
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/snippets/openapi/delete-browsers-id-fs-watch-watch_id.mdx b/snippets/openapi/delete-browsers-id-fs-watch-watch_id.mdx
new file mode 100644
index 0000000..95a91e2
--- /dev/null
+++ b/snippets/openapi/delete-browsers-id-fs-watch-watch_id.mdx
@@ -0,0 +1,24 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+await client.browsers.fs.watch.stop('watch_id', { id: 'id' });
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+client.browsers.fs.watch.stop(
+ watch_id="watch_id",
+ id="id",
+)
+```
+
diff --git a/snippets/openapi/delete-browsers-id.mdx b/snippets/openapi/delete-browsers-id.mdx
new file mode 100644
index 0000000..cdf3340
--- /dev/null
+++ b/snippets/openapi/delete-browsers-id.mdx
@@ -0,0 +1,23 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+await client.browsers.deleteByID('htzv5orfit78e1m2biiifpbv');
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+client.browsers.delete_by_id(
+ "id",
+)
+```
+
diff --git a/snippets/openapi/delete-browsers.mdx b/snippets/openapi/delete-browsers.mdx
new file mode 100644
index 0000000..09f6c4f
--- /dev/null
+++ b/snippets/openapi/delete-browsers.mdx
@@ -0,0 +1,23 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+await client.browsers.delete({ persistent_id: 'persistent_id' });
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+client.browsers.delete(
+ persistent_id="persistent_id",
+)
+```
+
diff --git a/snippets/openapi/delete-invocations-id-browsers.mdx b/snippets/openapi/delete-invocations-id-browsers.mdx
new file mode 100644
index 0000000..9f8806c
--- /dev/null
+++ b/snippets/openapi/delete-invocations-id-browsers.mdx
@@ -0,0 +1,23 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+await client.invocations.deleteBrowsers('id');
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+client.invocations.delete_browsers(
+ "id",
+)
+```
+
diff --git a/snippets/openapi/get-apps.mdx b/snippets/openapi/get-apps.mdx
new file mode 100644
index 0000000..e86399f
--- /dev/null
+++ b/snippets/openapi/get-apps.mdx
@@ -0,0 +1,24 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+const apps = await client.apps.list();
+
+console.log(apps);
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+apps = client.apps.list()
+print(apps)
+```
+
diff --git a/snippets/openapi/get-browsers-id-fs-file_info.mdx b/snippets/openapi/get-browsers-id-fs-file_info.mdx
new file mode 100644
index 0000000..7a8875e
--- /dev/null
+++ b/snippets/openapi/get-browsers-id-fs-file_info.mdx
@@ -0,0 +1,27 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+const response = await client.browsers.fs.fileInfo('id', { path: '/J!' });
+
+console.log(response.is_dir);
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+response = client.browsers.fs.file_info(
+ id="id",
+ path="/J!",
+)
+print(response.is_dir)
+```
+
diff --git a/snippets/openapi/get-browsers-id-fs-list_files.mdx b/snippets/openapi/get-browsers-id-fs-list_files.mdx
new file mode 100644
index 0000000..60d0851
--- /dev/null
+++ b/snippets/openapi/get-browsers-id-fs-list_files.mdx
@@ -0,0 +1,27 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+const response = await client.browsers.fs.listFiles('id', { path: '/J!' });
+
+console.log(response);
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+response = client.browsers.fs.list_files(
+ id="id",
+ path="/J!",
+)
+print(response)
+```
+
diff --git a/snippets/openapi/get-browsers-id-fs-read_file.mdx b/snippets/openapi/get-browsers-id-fs-read_file.mdx
new file mode 100644
index 0000000..55976f8
--- /dev/null
+++ b/snippets/openapi/get-browsers-id-fs-read_file.mdx
@@ -0,0 +1,32 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+const response = await client.browsers.fs.readFile('id', { path: '/J!' });
+
+console.log(response);
+
+const content = await response.blob();
+console.log(content);
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+response = client.browsers.fs.read_file(
+ id="id",
+ path="/J!",
+)
+print(response)
+content = response.read()
+print(content)
+```
+
diff --git a/snippets/openapi/get-browsers-id-fs-watch-watch_id-events.mdx b/snippets/openapi/get-browsers-id-fs-watch-watch_id-events.mdx
new file mode 100644
index 0000000..fb4d252
--- /dev/null
+++ b/snippets/openapi/get-browsers-id-fs-watch-watch_id-events.mdx
@@ -0,0 +1,27 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+const response = await client.browsers.fs.watch.events('watch_id', { id: 'id' });
+
+console.log(response.path);
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+response = client.browsers.fs.watch.events(
+ watch_id="watch_id",
+ id="id",
+)
+print(response.path)
+```
+
diff --git a/snippets/openapi/get-browsers-id-replays-replay_id.mdx b/snippets/openapi/get-browsers-id-replays-replay_id.mdx
new file mode 100644
index 0000000..0c47cb2
--- /dev/null
+++ b/snippets/openapi/get-browsers-id-replays-replay_id.mdx
@@ -0,0 +1,32 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+const response = await client.browsers.replays.download('replay_id', { id: 'id' });
+
+console.log(response);
+
+const content = await response.blob();
+console.log(content);
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+response = client.browsers.replays.download(
+ replay_id="replay_id",
+ id="id",
+)
+print(response)
+content = response.read()
+print(content)
+```
+
diff --git a/snippets/openapi/get-browsers-id-replays.mdx b/snippets/openapi/get-browsers-id-replays.mdx
new file mode 100644
index 0000000..27eca8d
--- /dev/null
+++ b/snippets/openapi/get-browsers-id-replays.mdx
@@ -0,0 +1,26 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+const replays = await client.browsers.replays.list('id');
+
+console.log(replays);
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+replays = client.browsers.replays.list(
+ "id",
+)
+print(replays)
+```
+
diff --git a/snippets/openapi/get-browsers-id.mdx b/snippets/openapi/get-browsers-id.mdx
new file mode 100644
index 0000000..bc84e3b
--- /dev/null
+++ b/snippets/openapi/get-browsers-id.mdx
@@ -0,0 +1,26 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+const browser = await client.browsers.retrieve('htzv5orfit78e1m2biiifpbv');
+
+console.log(browser.session_id);
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+browser = client.browsers.retrieve(
+ "id",
+)
+print(browser.session_id)
+```
+
diff --git a/snippets/openapi/get-browsers.mdx b/snippets/openapi/get-browsers.mdx
new file mode 100644
index 0000000..ffc2c1c
--- /dev/null
+++ b/snippets/openapi/get-browsers.mdx
@@ -0,0 +1,24 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+const browsers = await client.browsers.list();
+
+console.log(browsers);
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+browsers = client.browsers.list()
+print(browsers)
+```
+
diff --git a/snippets/openapi/get-deployments-id-events.mdx b/snippets/openapi/get-deployments-id-events.mdx
new file mode 100644
index 0000000..b40d565
--- /dev/null
+++ b/snippets/openapi/get-deployments-id-events.mdx
@@ -0,0 +1,26 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+const response = await client.deployments.follow('id');
+
+console.log(response);
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+response = client.deployments.follow(
+ id="id",
+)
+print(response)
+```
+
diff --git a/snippets/openapi/get-deployments-id.mdx b/snippets/openapi/get-deployments-id.mdx
new file mode 100644
index 0000000..36935ca
--- /dev/null
+++ b/snippets/openapi/get-deployments-id.mdx
@@ -0,0 +1,26 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+const deployment = await client.deployments.retrieve('id');
+
+console.log(deployment.id);
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+deployment = client.deployments.retrieve(
+ "id",
+)
+print(deployment.id)
+```
+
diff --git a/snippets/openapi/get-deployments.mdx b/snippets/openapi/get-deployments.mdx
new file mode 100644
index 0000000..7cfb64f
--- /dev/null
+++ b/snippets/openapi/get-deployments.mdx
@@ -0,0 +1,24 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+const deployments = await client.deployments.list();
+
+console.log(deployments);
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+deployments = client.deployments.list()
+print(deployments)
+```
+
diff --git a/snippets/openapi/get-invocations-id-events.mdx b/snippets/openapi/get-invocations-id-events.mdx
new file mode 100644
index 0000000..fb08ac8
--- /dev/null
+++ b/snippets/openapi/get-invocations-id-events.mdx
@@ -0,0 +1,26 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+const response = await client.invocations.follow('id');
+
+console.log(response);
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+response = client.invocations.follow(
+ "id",
+)
+print(response)
+```
+
diff --git a/snippets/openapi/get-invocations-id.mdx b/snippets/openapi/get-invocations-id.mdx
new file mode 100644
index 0000000..7dbd194
--- /dev/null
+++ b/snippets/openapi/get-invocations-id.mdx
@@ -0,0 +1,26 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+const invocation = await client.invocations.retrieve('rr33xuugxj9h0bkf1rdt2bet');
+
+console.log(invocation.id);
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+invocation = client.invocations.retrieve(
+ "id",
+)
+print(invocation.id)
+```
+
diff --git a/snippets/openapi/patch-invocations-id.mdx b/snippets/openapi/patch-invocations-id.mdx
new file mode 100644
index 0000000..6eebf72
--- /dev/null
+++ b/snippets/openapi/patch-invocations-id.mdx
@@ -0,0 +1,27 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+const invocation = await client.invocations.update('id', { status: 'succeeded' });
+
+console.log(invocation.id);
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+invocation = client.invocations.update(
+ id="id",
+ status="succeeded",
+)
+print(invocation.id)
+```
+
diff --git a/snippets/openapi/post-browsers-id-fs-watch.mdx b/snippets/openapi/post-browsers-id-fs-watch.mdx
new file mode 100644
index 0000000..fcd1dbf
--- /dev/null
+++ b/snippets/openapi/post-browsers-id-fs-watch.mdx
@@ -0,0 +1,27 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+const response = await client.browsers.fs.watch.start('id', { path: 'path' });
+
+console.log(response.watch_id);
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+response = client.browsers.fs.watch.start(
+ id="id",
+ path="path",
+)
+print(response.watch_id)
+```
+
diff --git a/snippets/openapi/post-browsers-id-replays-replay_id-stop.mdx b/snippets/openapi/post-browsers-id-replays-replay_id-stop.mdx
new file mode 100644
index 0000000..7658510
--- /dev/null
+++ b/snippets/openapi/post-browsers-id-replays-replay_id-stop.mdx
@@ -0,0 +1,24 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+await client.browsers.replays.stop('replay_id', { id: 'id' });
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+client.browsers.replays.stop(
+ replay_id="replay_id",
+ id="id",
+)
+```
+
diff --git a/snippets/openapi/post-browsers-id-replays.mdx b/snippets/openapi/post-browsers-id-replays.mdx
new file mode 100644
index 0000000..4b7a515
--- /dev/null
+++ b/snippets/openapi/post-browsers-id-replays.mdx
@@ -0,0 +1,26 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+const response = await client.browsers.replays.start('id');
+
+console.log(response.replay_id);
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+response = client.browsers.replays.start(
+ id="id",
+)
+print(response.replay_id)
+```
+
diff --git a/snippets/openapi/post-browsers-live-view.mdx b/snippets/openapi/post-browsers-live-view.mdx
new file mode 100644
index 0000000..b511024
--- /dev/null
+++ b/snippets/openapi/post-browsers-live-view.mdx
@@ -0,0 +1,24 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+const browser = await client.browsers.create();
+
+console.log(browser.browser_live_view_url);
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+browser = client.browsers.create()
+print(browser.browser_live_view_url)
+```
+
diff --git a/snippets/openapi/post-browsers-timeout.mdx b/snippets/openapi/post-browsers-timeout.mdx
new file mode 100644
index 0000000..d5e0644
--- /dev/null
+++ b/snippets/openapi/post-browsers-timeout.mdx
@@ -0,0 +1,24 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+const browser = await client.browsers.create({timeout_seconds: 300 });
+
+console.log(browser.session_id);
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+browser = client.browsers.create(timeout_seconds=300)
+print(browser.session_id)
+```
+
diff --git a/snippets/openapi/post-browsers.mdx b/snippets/openapi/post-browsers.mdx
new file mode 100644
index 0000000..264136b
--- /dev/null
+++ b/snippets/openapi/post-browsers.mdx
@@ -0,0 +1,24 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+const browser = await client.browsers.create();
+
+console.log(browser.session_id);
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+browser = client.browsers.create()
+print(browser.session_id)
+```
+
diff --git a/snippets/openapi/post-deployments.mdx b/snippets/openapi/post-deployments.mdx
new file mode 100644
index 0000000..6212992
--- /dev/null
+++ b/snippets/openapi/post-deployments.mdx
@@ -0,0 +1,30 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+const deployment = await client.deployments.create({
+ entrypoint_rel_path: 'src/app.py',
+ file: fs.createReadStream('path/to/file'),
+});
+
+console.log(deployment.id);
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+deployment = client.deployments.create(
+ entrypoint_rel_path="src/app.py",
+ file=b"raw file contents",
+)
+print(deployment.id)
+```
+
diff --git a/snippets/openapi/post-invocations-async.mdx b/snippets/openapi/post-invocations-async.mdx
new file mode 100644
index 0000000..1e62844
--- /dev/null
+++ b/snippets/openapi/post-invocations-async.mdx
@@ -0,0 +1,33 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+const invocation = await client.invocations.create({
+ async: true,
+ action_name: 'analyze',
+ app_name: 'my-app',
+ version: '1.0.0' });
+
+console.log(invocation.id);
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+invocation = client.invocations.create(
+ action_name="analyze",
+ app_name="my-app",
+ version="1.0.0",
+ async=True,
+)
+print(invocation.id)
+```
+
diff --git a/snippets/openapi/post-invocations.mdx b/snippets/openapi/post-invocations.mdx
new file mode 100644
index 0000000..31759ec
--- /dev/null
+++ b/snippets/openapi/post-invocations.mdx
@@ -0,0 +1,32 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+const invocation = await client.invocations.create({
+ action_name: 'analyze',
+ app_name: 'my-app',
+ version: '1.0.0',
+});
+
+console.log(invocation.id);
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+invocation = client.invocations.create(
+ action_name="analyze",
+ app_name="my-app",
+ version="1.0.0",
+)
+print(invocation.id)
+```
+
diff --git a/snippets/openapi/put-browsers-id-fs-create_directory.mdx b/snippets/openapi/put-browsers-id-fs-create_directory.mdx
new file mode 100644
index 0000000..c7607d3
--- /dev/null
+++ b/snippets/openapi/put-browsers-id-fs-create_directory.mdx
@@ -0,0 +1,24 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+await client.browsers.fs.createDirectory('id', { path: '/J!' });
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+client.browsers.fs.create_directory(
+ id="id",
+ path="/J!",
+)
+```
+
diff --git a/snippets/openapi/put-browsers-id-fs-delete_directory.mdx b/snippets/openapi/put-browsers-id-fs-delete_directory.mdx
new file mode 100644
index 0000000..a2758b6
--- /dev/null
+++ b/snippets/openapi/put-browsers-id-fs-delete_directory.mdx
@@ -0,0 +1,24 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+await client.browsers.fs.deleteDirectory('id', { path: '/J!' });
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+client.browsers.fs.delete_directory(
+ id="id",
+ path="/J!",
+)
+```
+
diff --git a/snippets/openapi/put-browsers-id-fs-delete_file.mdx b/snippets/openapi/put-browsers-id-fs-delete_file.mdx
new file mode 100644
index 0000000..ee84b33
--- /dev/null
+++ b/snippets/openapi/put-browsers-id-fs-delete_file.mdx
@@ -0,0 +1,24 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+await client.browsers.fs.deleteFile('id', { path: '/J!' });
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+client.browsers.fs.delete_file(
+ id="id",
+ path="/J!",
+)
+```
+
diff --git a/snippets/openapi/put-browsers-id-fs-move.mdx b/snippets/openapi/put-browsers-id-fs-move.mdx
new file mode 100644
index 0000000..8fc4cfb
--- /dev/null
+++ b/snippets/openapi/put-browsers-id-fs-move.mdx
@@ -0,0 +1,25 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+await client.browsers.fs.move('id', { dest_path: '/J!', src_path: '/J!' });
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+client.browsers.fs.move(
+ id="id",
+ dest_path="/J!",
+ src_path="/J!",
+)
+```
+
diff --git a/snippets/openapi/put-browsers-id-fs-set_file_permissions.mdx b/snippets/openapi/put-browsers-id-fs-set_file_permissions.mdx
new file mode 100644
index 0000000..f898c3b
--- /dev/null
+++ b/snippets/openapi/put-browsers-id-fs-set_file_permissions.mdx
@@ -0,0 +1,25 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+await client.browsers.fs.setFilePermissions('id', { mode: '0611', path: '/J!' });
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+client.browsers.fs.set_file_permissions(
+ id="id",
+ mode="0611",
+ path="/J!",
+)
+```
+
diff --git a/snippets/openapi/put-browsers-id-fs-write_file.mdx b/snippets/openapi/put-browsers-id-fs-write_file.mdx
new file mode 100644
index 0000000..9b6c5de
--- /dev/null
+++ b/snippets/openapi/put-browsers-id-fs-write_file.mdx
@@ -0,0 +1,25 @@
+
+```typescript Typescript/Javascript
+import Kernel from '@onkernel/sdk';
+
+const client = new Kernel({
+ apiKey: 'My API Key',
+});
+
+await client.browsers.fs.writeFile('id', fs.createReadStream('path/to/file'), { path: '/J!' });
+```
+
+
+```python Python
+from kernel import Kernel
+
+client = Kernel(
+ api_key="My API Key",
+)
+client.browsers.fs.write_file(
+ id="id",
+ contents=b"raw file contents",
+ path="/J!",
+)
+```
+