// Zendy slash commands — /zendy-config, /zendy-status. // Loaded by jiti as part of the zendy extension. import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { getConfig, writeConfig, configPath, configExists } from "../dist/config/store.js"; import { migrateLegacyConfig } from "../dist/config/migrate.js"; import type { ZendyConfig } from "../dist/config/schema.js"; import { DEFAULT_KG_API_URL } from "../dist/config/schema.js"; import * as zendesk from "../dist/clients/zendesk.js"; import * as kg from "../dist/clients/zendesk-kg.js"; async function configCommand(_args: string, ctx: { ui: { select: (prompt: string, options: string[]) => Promise; input: (prompt: string, placeholder?: string) => Promise; notify: (msg: string, level: string) => void; }; }): Promise { const migrated = migrateLegacyConfig(); if (migrated?.migrated) { ctx.ui.notify(`[zendy-config] Auto-imported credentials from ${migrated.source}`, "info"); } const action = await ctx.ui.select("Zendy Config — what would you like to configure?", [ "Zendesk credentials", "Knowledge Graph credentials", "View current config", ]); if (!action) return; const config: ZendyConfig = getConfig(); if (action === "Zendesk credentials") { const subdomain = await ctx.ui.input("Zendesk subdomain (e.g., dify)", config.zendesk?.subdomain ?? "dify"); const email = await ctx.ui.input("Zendesk email address", config.zendesk?.email ?? ""); const apiToken = await ctx.ui.input("Zendesk API token", ""); config.zendesk = { ...config.zendesk, subdomain: subdomain || config.zendesk?.subdomain, email: email || config.zendesk?.email, apiToken: apiToken || config.zendesk?.apiToken, }; writeConfig(config); ctx.ui.notify("[zendy-config] Zendesk credentials saved.", "info"); } else if (action === "Knowledge Graph credentials") { const apiUrl = await ctx.ui.input( "Knowledge Graph API URL (leave empty for default)", config.zendeskKg?.apiUrl ?? DEFAULT_KG_API_URL, ); const apiKey = await ctx.ui.input("Knowledge Graph API key", ""); config.zendeskKg = { ...config.zendeskKg, apiUrl: apiUrl || config.zendeskKg?.apiUrl, apiKey: apiKey || config.zendeskKg?.apiKey, }; writeConfig(config); ctx.ui.notify("[zendy-config] Knowledge Graph credentials saved.", "info"); } else if (action === "View current config") { const c = getConfig(); const lines: string[] = []; lines.push(`Config file: ${configPath()}`); lines.push(`Exists: ${configExists() ? "yes" : "no"}`); lines.push( `Zendesk: ${c.zendesk?.subdomain ? `subdomain=${c.zendesk.subdomain}, email=${c.zendesk.email?.replace(/(.).+(@.+)/, "$1***$2") ?? "not set"}` : "not configured"}`, ); lines.push( `KG: ${c.zendeskKg?.apiKey ? `apiKey=*** (set), apiUrl=${c.zendeskKg.apiUrl || "default"}` : "not configured"}`, ); const repoEntries = Object.entries(c.sourceRepos ?? {}); const privateCount = repoEntries.filter(([, repo]) => repo.visibility === "private").length; lines.push(`Source repos: ${repoEntries.length} configured (${privateCount} private SSH-gated)`); ctx.ui.notify(`[zendy-config]\n${lines.join("\n")}`, "info"); } } async function statusCommand(_args: string, ctx: { ui: { notify: (msg: string, level: "info" | "error" | "warn") => void }; }): Promise { const lines: string[] = []; let hasIssue = false; try { const me = await zendesk.getMe(); lines.push(` ✓ Zendesk — authenticated as ${me.user.email}`); } catch (e) { lines.push(` ✗ Zendesk — ${(e as Error).message.split("\n")[0]}`); hasIssue = true; } try { const health = await kg.healthCheck(); lines.push(` ✓ Knowledge Graph — ${health.status}`); } catch (e) { lines.push(` ✗ Knowledge Graph — ${(e as Error).message.split("\n")[0]}`); hasIssue = true; } try { const r = await fetch("https://dify-helm-watchdog.vercel.app/api/v1/versions/latest?versionOnly=true"); lines.push(` ✓ Helm Watchdog — latest chart: ${(await r.text()).trim()}`); } catch (e) { lines.push(` ✗ Helm Watchdog — ${(e as Error).message.split("\n")[0]}`); hasIssue = true; } const srcDir = process.env["ZENDY_SRC_DIR"]; lines.push( srcDir ? ` ✓ Source workspace — ${srcDir}` : " - Source workspace — not active (will be created on session start)", ); const repoEntries = Object.entries(getConfig().sourceRepos ?? {}); const privateCount = repoEntries.filter(([, repo]) => repo.visibility === "private").length; lines.push(` ✓ Source repos — ${repoEntries.length} configured (${privateCount} private SSH-gated)`); ctx.ui.notify(`Zendy Status:\n${lines.join("\n")}`, hasIssue ? "error" : "info"); } export function registerAllCommands(pi: ExtensionAPI): void { pi.registerCommand("zendy-config", { description: "Configure Zendesk and Knowledge Graph credentials", handler: configCommand, }); pi.registerCommand("zendy-status", { description: "Check Zendy connectivity: Zendesk, KG, Helm Watchdog", handler: statusCommand, }); }