// Name: Parse n8n Node to Code // Description: Convert an n8n node JSON into runnable code (fetch/axios). // Author: tayiorbeii // GitHub: tayiorbeii import "@johnlindquist/kit" type N8nNode = { id?: string name?: string type?: string parameters?: any } const isJson = (s: string) => { try { JSON.parse(s) return true } catch { return false } } const tryClipboard = await paste() const initialJson = isJson(tryClipboard?.trim?.() || "") ? tryClipboard : `{ "type": "n8n-nodes-base.httpRequest", "name": "HTTP Request", "parameters": { "method": "GET", "url": "https://api.example.com/items", "queryParametersUi": { "parameter": [ { "name": "page", "value": "1" } ] }, "headersUi": { "parameter": [ { "name": "Accept", "value": "application/json" } ] } } }` const raw = await editor({ value: initialJson, language: "json", hint: "Paste an n8n node (or workflow export). Press cmd+s to parse.", footer: "Supports HTTP Request and Function nodes. Others will be summarized.", }) let parsed: any try { parsed = JSON.parse(raw) } catch (e: any) { await div(md(`❌ JSON parse error:\n\n\`${e?.message || e}\``)) exit() } let node: N8nNode | undefined if (Array.isArray(parsed?.nodes)) { const choices = parsed.nodes.map((n: N8nNode, i: number) => ({ name: `${n.name || `Node ${i + 1}`} · ${n.type}`, description: n.id ? `id: ${n.id}` : "", value: n, })) node = await arg("Select an n8n node", choices) } else if (parsed?.type) { node = parsed } else if (Array.isArray(parsed)) { // If user pasted an array of nodes const choices = parsed.map((n: N8nNode, i: number) => ({ name: `${n.name || `Node ${i + 1}`} · ${n.type}`, value: n, })) node = await arg("Select an n8n node", choices) } else if (parsed?.nodes == null && parsed?.data?.nodes) { // Some exports wrap nodes under data const choices = parsed.data.nodes.map((n: N8nNode, i: number) => ({ name: `${n.name || `Node ${i + 1}`} · ${n.type}`, value: n, })) node = await arg("Select an n8n node", choices) } if (!node) { await div(md(`❌ Could not find a node in your JSON.`)) exit() } const toObjectFromUiParams = (obj: any, key: string) => { const list = obj?.[key]?.parameter if (!Array.isArray(list)) return {} const out: Record<string, any> = {} for (const p of list) { if (p?.name != null && p?.name !== "") { out[String(p.name)] = p?.value } } return out } const cleanEmpty = (o: any) => { if (!o || typeof o !== "object") return o const out: any = Array.isArray(o) ? [] : {} for (const [k, v] of Object.entries(o)) { if (v === undefined || v === null || v === "") continue if (typeof v === "object") { const c = cleanEmpty(v) const isEmptyObj = typeof c === "object" && c && Object.keys(c).length === 0 if (!isEmptyObj) out[k] = c } else { out[k] = v } } return out } const jsonString = (v: any) => JSON.stringify(v, null, 2) const genHttpAxios = (n: N8nNode) => { const p = n.parameters || {} const method = (p.method || "GET").toUpperCase() const url = p.url || p.uri || "" const headers = { ...toObjectFromUiParams(p, "headersUi"), } const params = { ...toObjectFromUiParams(p, "queryParametersUi"), } let data: any = undefined // JSON parameters case if (p.jsonParameters) { if (typeof p.bodyParametersJson === "string" && p.bodyParametersJson.trim()) { try { data = JSON.parse(p.bodyParametersJson) } catch { data = p.bodyParametersJson } } else if (p.options?.bodyContentType === "form-urlencoded") { data = toObjectFromUiParams(p, "options") // unlikely, fallback } } else { // UI parameters case const uiBody = toObjectFromUiParams(p, "bodyParametersUi") if (Object.keys(uiBody).length) data = uiBody } const hasBody = !["GET", "HEAD"].includes(method) && data !== undefined const cfgPieces: string[] = [] cfgPieces.push(`method: ${jsonString(method)}`) cfgPieces.push(`url: ${jsonString(url)}`) if (Object.keys(params).length) cfgPieces.push(`params: ${jsonString(params)}`) if (Object.keys(headers).length) cfgPieces.push(`headers: ${jsonString(headers)}`) if (hasBody) cfgPieces.push(`data: ${jsonString(data)}`) return `import axios from "axios" const res = await axios({ ${cfgPieces.join(",\n ")} }) console.log(res.status, res.data) ` } const genHttpFetch = (n: N8nNode) => { const p = n.parameters || {} const method = (p.method || "GET").toUpperCase() const baseUrl = p.url || p.uri || "" const headers = { ...toObjectFromUiParams(p, "headersUi"), } const params = { ...toObjectFromUiParams(p, "queryParametersUi"), } let data: any = undefined let isJsonBody = true if (p.jsonParameters) { if (typeof p.bodyParametersJson === "string" && p.bodyParametersJson.trim()) { try { data = JSON.parse(p.bodyParametersJson) } catch { data = p.bodyParametersJson isJsonBody = false } } } else { const uiBody = toObjectFromUiParams(p, "bodyParametersUi") if (Object.keys(uiBody).length) data = uiBody } const buildBody = () => { if (data === undefined || ["GET", "HEAD"].includes(method)) return "undefined" if (typeof data === "string") return "body" return "JSON.stringify(body)" } const bodyDecl = data === undefined ? "" : `const body = ${typeof data === "string" ? jsonString(data) : jsonString(data)}\n` const ensureHeaders = data !== undefined && typeof data !== "string" ? `\nheaders["Content-Type"] = headers["Content-Type"] || "application/json"` : "" return `const headers: Record<string, string> = ${jsonString(headers)} const params = ${jsonString(params)} const baseUrl = ${jsonString(baseUrl)} ${bodyDecl}const u = new URL(baseUrl) Object.entries(params).forEach(([k, v]) => { if (v !== undefined && v !== null) u.searchParams.append(k, String(v)) })${ensureHeaders} const res = await fetch(u.toString(), { method: ${jsonString(method)}, headers, body: ${buildBody()} }) if (!res.ok) { throw new Error(\`HTTP \${res.status} \${res.statusText}\`) } const data = await res.json().catch(async () => await res.text()) console.log(res.status, data) ` } const genFunctionNode = (n: N8nNode) => { const code = n?.parameters?.functionCode || n?.parameters?.code || "// No functionCode found" return `// n8n Function Node code extracted ${String(code).trim()} ` } const summarizeUnknown = (n: N8nNode) => { return `// Unsupported node type: ${n.type} // Name: ${n.name || ""} const node = ${jsonString(cleanEmpty(n))} ` } const style = await arg("Select output style", [ { name: "fetch (native)", value: "fetch" }, { name: "axios", value: "axios" }, ]) let output = "" if (node.type === "n8n-nodes-base.httpRequest") { output = style === "axios" ? genHttpAxios(node) : genHttpFetch(node) } else if (node.type === "n8n-nodes-base.function") { output = genFunctionNode(node) } else { output = summarizeUnknown(node) } await copy(output) await editor({ value: output, language: "typescript", hint: "Generated code copied to clipboard", footer: "You can paste this into your project.", })// Name: Format jsCode from Clipboard // Description: Converts a JSON-escaped jsCode string on the clipboard into formatted code and pastes it. // Author: tayiorbeii // GitHub: tayiorbeii import "@johnlindquist/kit" const raw = (await clipboard.readText())?.trim() if (!raw) { await notify("Clipboard is empty") exit() } // Try to extract "jsCode": "<string>" and decode it safely const extractJsCode = (input: string): string | null => { // Match the JSON string literal assigned to "jsCode" (handles escaped quotes) const m = input.match(/"jsCode"\s*:\s*("(?:(?:\\.|[^"\\])*)")/) if (m) { try { // m[1] includes the surrounding quotes; JSON.parse will unescape \n, \t, \", etc. return JSON.parse(m[1]) } catch { // fall through } } return null } // Decode a C-style escaped string (e.g., contains \n, \t, \") using JSON trick const decodeCStyle = (s: string): string => { try { // If it's already a valid JSON string literal, parse directly if ((s.startsWith('"') && s.endsWith('"')) || (s.startsWith("'") && s.endsWith("'"))) { // JSON requires double quotes. If single-quoted, convert a simple case: if (s.startsWith("'") && s.endsWith("'")) { const dbl = `"${s.slice(1, -1).replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\r/g, "\\r").replace(/\n/g, "\\n")}"` return JSON.parse(dbl) } return JSON.parse(s) } // Otherwise, wrap it to form a JSON string literal and parse const wrapped = `"${s .replace(/\\/g, "\\\\") .replace(/"/g, '\\"') .replace(/\r/g, "\\r") .replace(/\n/g, "\\n")}"` return JSON.parse(wrapped) } catch { // Basic fallback replacements return s .replace(/\\r\\n/g, "\n") .replace(/\\n/g, "\n") .replace(/\\t/g, "\t") .replace(/\\"/g, `"`) .replace(/\\'/g, `'`) .replace(/\\\\/g, "\\") } } let code = extractJsCode(raw) // If "jsCode" key wasn't found, try to treat clipboard as an escaped string or raw code if (!code) { code = decodeCStyle(raw) } // Optional: trim a single trailing newline at end for neatness if (code.endsWith("\n")) { code = code.replace(/\n+$/, "\n") } await clipboard.writeText(code) await setSelectedText(code) await toast("Formatted code pasted from clipboard")// Name: Call Generate for Matching Chrome Tabs // Description: Finds Chrome tabs with titles containing a word and runs the 'generate' script for each match. // Author: tayiorbeii // GitHub: tayiorbeii import '@johnlindquist/kit' const keyword = (await arg('Enter word to match in Chrome tab titles')).trim() if (!keyword) exit() let tabs = [] try { tabs = await getTabs('Google Chrome') } catch (e) { await notify('Unable to read Chrome tabs') exit() } const matches = tabs.filter(t => (t.title || '').toLowerCase().includes(keyword.toLowerCase())) for (const tab of matches) { await run('generate', tab.title ?? '', tab.url ?? '') }