@tplog/pi-zendy 0.2.17 → 0.3.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # zendy
2
2
 
3
- Pi package for Dify Enterprise support ticket analysis. Analyze Zendesk tickets with natural language — from ticket metadata to Helm chart values to source code.
3
+ Pi extension for Dify Enterprise support ticket analysis. Analyze Zendesk tickets with natural language — from ticket metadata to Helm chart values to source code.
4
4
 
5
5
  Powered by [pi](https://pi.dev).
6
6
 
@@ -8,25 +8,23 @@ Powered by [pi](https://pi.dev).
8
8
 
9
9
  ## What it does
10
10
 
11
- zendy bundles support skills and extensions into a Pi package:
11
+ zendy is a single pi extension that provides:
12
12
 
13
- - **zcli** — Pull Zendesk ticket metadata and comment threads
14
- - **helm-watchdog** — Query Dify Helm chart values, images, and validation for any version
15
- - **source-check** — Clone and analyze Dify source code (enterprise backend/frontend, open-source core, plugin daemon, sandbox)
13
+ - **LLM Tools** — Direct API access to Zendesk, Helm Watchdog, and Knowledge Graph. No external CLI dependencies.
14
+ - **Slash Commands** — `/zendy-config` to set up credentials, `/zendy-status` to check connectivity, `/zendy-cleanup` to wipe source clones.
15
+ - **Session Safety** — Automatic workspace isolation and cleanup for source code analysis.
16
16
 
17
17
  Typical workflow:
18
18
 
19
19
  ```
20
- "Analyze ticket #1959" → pull ticket + comments identify version →
21
- pull Helm chart values analyze config (if needed) clone source
22
- synthesize findings → draft reply
20
+ pi → "Analyze ticket #1959" → agent calls zendy_ticket_get
21
+ identifies versionagent calls zendy_helm_get
22
+ synthesizes findings → drafts reply
23
23
  ```
24
24
 
25
25
  ## Prerequisites
26
26
 
27
27
  - [pi](https://pi.dev) installed globally: `npm install -g @earendil-works/pi-coding-agent`
28
- - Zendesk credentials configured: `zcli configure` or env vars (`ZENDESK_SUBDOMAIN`, `ZENDESK_EMAIL`, `ZENDESK_API_TOKEN`)
29
- - Git SSH access to private repos (for source-check skill)
30
28
 
31
29
  ## Install
32
30
 
@@ -34,31 +32,63 @@ synthesize findings → draft reply
34
32
  pi install npm:@tplog/pi-zendy
35
33
  ```
36
34
 
37
- Or clone and build from source:
35
+ ## Configure
38
36
 
39
- ```bash
40
- git clone git@github.com:tplog/pi-zendy.git
41
- cd zendy
42
- npm install
43
- npm run build
44
- npm link
37
+ Start pi and run:
38
+
39
+ ```
40
+ /zendy-config
45
41
  ```
46
42
 
47
- ## Usage
43
+ This interactively collects Zendesk credentials (subdomain, email, API token) and Knowledge Graph API key.
48
44
 
49
- After installing the Pi package, start pi normally:
45
+ Alternatively, set environment variables:
50
46
 
51
47
  ```bash
52
- pi
48
+ export ZENDY_ZENDESK_SUBDOMAIN=dify
49
+ export ZENDY_ZENDESK_EMAIL=you@example.com
50
+ export ZENDY_ZENDESK_API_TOKEN=your_token
51
+ export ZENDY_KG_API_KEY=your_kg_key
53
52
  ```
54
53
 
55
- You can also install the optional legacy launcher:
54
+ Credentials are stored in `~/.zendy/config.json` (mode 0600). On first run, zendy auto-imports
55
+ from legacy `zcli` and `zendesk-kg` config files if they exist.
56
+
57
+ ## Commands
58
+
59
+ | Command | Purpose |
60
+ |---------|---------|
61
+ | `/zendy-config` | Configure Zendesk and KG credentials |
62
+ | `/zendy-status` | Check connectivity to all services |
63
+ | `/zendy-cleanup` | Wipe source clones and orphan session dirs |
64
+
65
+ ## Tools
66
+
67
+ The agent can call these tools directly:
68
+
69
+ | Tool | Description |
70
+ |------|-------------|
71
+ | `zendy_ticket_get` | Fetch ticket metadata, comments, and user info |
72
+ | `zendy_ticket_search` | Search live Zendesk tickets |
73
+ | `zendy_helm_get` | Query Helm chart values, images, validation by version |
74
+ | `zendy_kg_search` | Semantic search over historical tickets |
75
+ | `zendy_source_status` | Check source analysis workspace |
76
+
77
+ ## Legacy Launcher
78
+
79
+ For users of the old `zendy` CLI:
56
80
 
57
81
  ```bash
58
82
  npm install -g @tplog/pi-zendy
59
83
  zendy
60
84
  ```
61
85
 
86
+ The legacy launcher starts pi with the zendy extension and system prompt. It still supports
87
+ `zendy preflight` and `zendy cleanup-src` as standalone subcommands.
88
+
62
89
  ## How it works
63
90
 
64
- As a Pi package, zendy contributes its skills and extensions through the package manifest. The legacy `zendy` launcher still exists for compatibility and starts `pi` with zendy's bundled resources while keeping user/global extensions available.
91
+ zendy registers as a pi extension package. The extension provides tools (callable by the LLM),
92
+ slash commands (for human engineers), and session lifecycle hooks (workspace creation, cleanup).
93
+ All data access goes through direct REST APIs — no `zcli`, `zendesk-kg`, or other CLI tools
94
+ are required at runtime.
@@ -0,0 +1,7 @@
1
+ export declare function getVersion(version: string, signal?: AbortSignal): Promise<Record<string, unknown>>;
2
+ export declare function getValues(version: string, signal?: AbortSignal): Promise<string>;
3
+ export declare function getImages(version: string, validate?: boolean, signal?: AbortSignal): Promise<Record<string, unknown>[]>;
4
+ export declare function getValidation(version: string, status?: string, signal?: AbortSignal): Promise<Record<string, unknown>[]>;
5
+ export declare function getLatest(versionOnly?: boolean, signal?: AbortSignal): Promise<string | Record<string, unknown>>;
6
+ export declare function listVersions(signal?: AbortSignal): Promise<Record<string, unknown>[]>;
7
+ export declare function getCache(signal?: AbortSignal): Promise<Record<string, unknown>>;
@@ -0,0 +1,49 @@
1
+ // Direct Helm Watchdog API client. No curl/skill needed.
2
+ const BASE_URL = "https://dify-helm-watchdog.vercel.app/api/v1";
3
+ async function fetchJson(url, signal) {
4
+ const response = await fetch(url, {
5
+ headers: { Accept: "application/json", "User-Agent": "zendy/1.0" },
6
+ signal,
7
+ });
8
+ if (!response.ok) {
9
+ const body = await response.text().catch(() => "");
10
+ throw new Error(`Helm Watchdog error ${response.status}: ${body.slice(0, 500)}`);
11
+ }
12
+ const ct = response.headers.get("content-type") ?? "";
13
+ if (ct.includes("application/json")) {
14
+ return response.json();
15
+ }
16
+ return response.text();
17
+ }
18
+ export async function getVersion(version, signal) {
19
+ return fetchJson(`${BASE_URL}/versions/${encodeURIComponent(version)}`, signal);
20
+ }
21
+ export async function getValues(version, signal) {
22
+ const response = await fetch(`${BASE_URL}/versions/${encodeURIComponent(version)}/values`, {
23
+ headers: { Accept: "text/yaml, text/plain, */*", "User-Agent": "zendy/1.0" },
24
+ signal,
25
+ });
26
+ if (!response.ok) {
27
+ const body = await response.text().catch(() => "");
28
+ throw new Error(`Helm Watchdog error ${response.status}: ${body.slice(0, 500)}`);
29
+ }
30
+ return response.text();
31
+ }
32
+ export async function getImages(version, validate = false, signal) {
33
+ const params = validate ? "?validate=true" : "";
34
+ return fetchJson(`${BASE_URL}/versions/${encodeURIComponent(version)}/images${params}`, signal);
35
+ }
36
+ export async function getValidation(version, status, signal) {
37
+ const params = status ? `?status=${encodeURIComponent(status)}` : "";
38
+ return fetchJson(`${BASE_URL}/versions/${encodeURIComponent(version)}/validation${params}`, signal);
39
+ }
40
+ export async function getLatest(versionOnly = false, signal) {
41
+ const params = versionOnly ? "?versionOnly=true" : "";
42
+ return fetchJson(`${BASE_URL}/versions/latest${params}`, signal);
43
+ }
44
+ export async function listVersions(signal) {
45
+ return fetchJson(`${BASE_URL}/versions`, signal);
46
+ }
47
+ export async function getCache(signal) {
48
+ return fetchJson(`${BASE_URL}/cache`, signal);
49
+ }
@@ -0,0 +1,59 @@
1
+ export interface KgSearchResult {
2
+ ticketId: string;
3
+ subject: string;
4
+ status: string;
5
+ priority: string;
6
+ quickSummary: string;
7
+ issueSummary: string;
8
+ solutionSummary: string;
9
+ createdAt: string;
10
+ handledBy: string[];
11
+ versions: string[];
12
+ keywords: string[];
13
+ referenceUrls: string[];
14
+ channels: Record<string, unknown>;
15
+ rrfScore: number;
16
+ }
17
+ export interface KgSearchResponse {
18
+ results: KgSearchResult[];
19
+ queryText: string;
20
+ queryVectorDimension: number;
21
+ constraintsApplied: Record<string, unknown>;
22
+ fallbackSuggestion: string | null;
23
+ recommendedLinks: unknown[];
24
+ }
25
+ export interface KgStats {
26
+ tickets: number;
27
+ versions: number;
28
+ keywords: number;
29
+ links: number;
30
+ people: number;
31
+ }
32
+ export interface KgHealth {
33
+ status: string;
34
+ }
35
+ export interface KgSearchFilter {
36
+ versions?: string[];
37
+ priority?: string;
38
+ status?: string;
39
+ handledBy?: string;
40
+ keywords?: string[];
41
+ createdAfter?: string;
42
+ createdBefore?: string;
43
+ channels?: string[];
44
+ }
45
+ export interface KgSearchOptions {
46
+ query: string;
47
+ topK?: number;
48
+ vectorK?: number;
49
+ fulltextK?: number;
50
+ rrfK?: number;
51
+ orderBy?: "rrfScore" | "createdAt" | "priority";
52
+ allowVersionFamilyFallback?: boolean;
53
+ recommendedLinkK?: number;
54
+ useLlmClassification?: boolean;
55
+ filter?: KgSearchFilter;
56
+ }
57
+ export declare function search(options: KgSearchOptions, signal?: AbortSignal): Promise<KgSearchResponse>;
58
+ export declare function getStats(signal?: AbortSignal): Promise<KgStats>;
59
+ export declare function healthCheck(signal?: AbortSignal): Promise<KgHealth>;
@@ -0,0 +1,100 @@
1
+ // Direct Zendesk Knowledge Graph API client. No zendesk-kg CLI dependency.
2
+ import { getConfig } from "../config/store.js";
3
+ import { migrateLegacyConfig, getLegacyKgEnv } from "../config/migrate.js";
4
+ import { DEFAULT_KG_API_URL } from "../config/schema.js";
5
+ // ── Resolve config ─────────────────────────────────────────────────────
6
+ function resolveConfig() {
7
+ // Try zendy config first (env or ~/.zendy/config.json)
8
+ const { zendeskKg } = getConfig();
9
+ if (zendeskKg?.apiKey)
10
+ return zendeskKg;
11
+ // Try legacy migration
12
+ migrateLegacyConfig();
13
+ const cfg2 = getConfig();
14
+ if (cfg2.zendeskKg?.apiKey)
15
+ return cfg2.zendeskKg;
16
+ // Try legacy .env directly
17
+ const legacy = getLegacyKgEnv();
18
+ if (legacy?.["RETRIEVER_API_KEY"]) {
19
+ return {
20
+ apiUrl: legacy["RETRIEVER_API_URL"] || undefined,
21
+ apiKey: legacy["RETRIEVER_API_KEY"],
22
+ };
23
+ }
24
+ throw new Error("Zendesk KG not configured. Use /zendy-config to set up credentials.\n" +
25
+ "Or set env: ZENDY_KG_API_KEY");
26
+ }
27
+ // ── HTTP helpers ───────────────────────────────────────────────────────
28
+ async function kgPost(path, body, signal) {
29
+ const cfg = resolveConfig();
30
+ const base = cfg.apiUrl || DEFAULT_KG_API_URL;
31
+ const data = JSON.stringify(body);
32
+ const headers = {
33
+ "Content-Type": "application/json",
34
+ Accept: "application/json",
35
+ Authorization: `Bearer ${cfg.apiKey}`,
36
+ "Content-Length": String(Buffer.byteLength(data)),
37
+ "User-Agent": "zendy/1.0",
38
+ };
39
+ if (cfg.apiKey)
40
+ headers["x-api-key"] = cfg.apiKey;
41
+ const response = await fetch(`${base}${path}`, {
42
+ method: "POST",
43
+ headers,
44
+ body: data,
45
+ signal,
46
+ });
47
+ if (!response.ok) {
48
+ const respBody = await response.text().catch(() => "");
49
+ throw new Error(`KG API error ${response.status}: ${respBody.slice(0, 500)}`);
50
+ }
51
+ return response.json();
52
+ }
53
+ async function kgGet(path, signal) {
54
+ const cfg = resolveConfig();
55
+ const base = cfg.apiUrl || DEFAULT_KG_API_URL;
56
+ const headers = {
57
+ Accept: "application/json",
58
+ Authorization: `Bearer ${cfg.apiKey}`,
59
+ "User-Agent": "zendy/1.0",
60
+ };
61
+ if (cfg.apiKey)
62
+ headers["x-api-key"] = cfg.apiKey;
63
+ const response = await fetch(`${base}${path}`, {
64
+ headers,
65
+ signal,
66
+ });
67
+ if (!response.ok) {
68
+ const body = await response.text().catch(() => "");
69
+ throw new Error(`KG API error ${response.status}: ${body.slice(0, 500)}`);
70
+ }
71
+ return response.json();
72
+ }
73
+ // ── Public API ─────────────────────────────────────────────────────────
74
+ export async function search(options, signal) {
75
+ const body = {
76
+ query: options.query,
77
+ topK: options.topK ?? 5,
78
+ vectorK: options.vectorK ?? 10,
79
+ fulltextK: options.fulltextK ?? 10,
80
+ rrfK: options.rrfK ?? 60,
81
+ allowVersionFamilyFallback: options.allowVersionFamilyFallback ?? false,
82
+ recommendedLinkK: options.recommendedLinkK ?? 3,
83
+ useLlmClassification: options.useLlmClassification ?? false,
84
+ };
85
+ if (options.filter && Object.keys(options.filter).length > 0) {
86
+ body.filter = options.filter;
87
+ }
88
+ return kgPost("/v1/retrieval", body, signal);
89
+ }
90
+ export async function getStats(signal) {
91
+ return kgGet("/stats", signal);
92
+ }
93
+ export async function healthCheck(signal) {
94
+ try {
95
+ return await kgGet("/health", signal);
96
+ }
97
+ catch {
98
+ return { status: "unreachable" };
99
+ }
100
+ }
@@ -0,0 +1,64 @@
1
+ export interface Ticket {
2
+ id: number;
3
+ url: string;
4
+ subject: string;
5
+ description: string;
6
+ status: string;
7
+ priority: string | null;
8
+ created_at: string;
9
+ updated_at: string;
10
+ requester_id: number;
11
+ assignee_id: number | null;
12
+ organization_id: number | null;
13
+ group_id: number | null;
14
+ tags: string[];
15
+ custom_fields: Array<{
16
+ id: number;
17
+ value: string | number | boolean | null;
18
+ }>;
19
+ [key: string]: unknown;
20
+ }
21
+ export interface SlimComment {
22
+ id: number;
23
+ type: string;
24
+ author_id: number;
25
+ body: string;
26
+ html_body: string;
27
+ plain_body: string;
28
+ public: boolean;
29
+ created_at: string;
30
+ [key: string]: unknown;
31
+ }
32
+ export interface User {
33
+ id: number;
34
+ name: string;
35
+ email: string;
36
+ role: string;
37
+ [key: string]: unknown;
38
+ }
39
+ export interface SearchResults {
40
+ results: Array<Record<string, unknown>>;
41
+ count: number;
42
+ next_page: string | null;
43
+ previous_page: string | null;
44
+ }
45
+ export interface TicketResult {
46
+ ticket: Ticket;
47
+ comments?: SlimComment[];
48
+ requester?: User;
49
+ assignee?: User;
50
+ }
51
+ export declare function getTicket(ticketId: number, signal?: AbortSignal): Promise<{
52
+ ticket: Ticket;
53
+ }>;
54
+ export declare function getTicketComments(ticketId: number, signal?: AbortSignal): Promise<{
55
+ comments: SlimComment[];
56
+ }>;
57
+ export declare function getUser(userId: number, signal?: AbortSignal): Promise<{
58
+ user: User;
59
+ }>;
60
+ export declare function searchTickets(query: string, signal?: AbortSignal): Promise<SearchResults>;
61
+ export declare function getMe(signal?: AbortSignal): Promise<{
62
+ user: User;
63
+ }>;
64
+ export declare function getTicketFull(ticketId: number, signal?: AbortSignal): Promise<TicketResult>;
@@ -0,0 +1,90 @@
1
+ // Direct Zendesk REST API client. No zcli dependency.
2
+ import { getConfig } from "../config/store.js";
3
+ import { migrateLegacyConfig, getLegacyZendeskConfig } from "../config/migrate.js";
4
+ // ── Resolve config ─────────────────────────────────────────────────────
5
+ function resolveConfig() {
6
+ // Try zendy config first (env or ~/.zendy/config.json)
7
+ const { zendesk } = getConfig();
8
+ if (zendesk?.subdomain && zendesk?.email && zendesk?.apiToken) {
9
+ return zendesk;
10
+ }
11
+ // Try legacy migration
12
+ migrateLegacyConfig();
13
+ const cfg2 = getConfig();
14
+ if (cfg2.zendesk?.subdomain && cfg2.zendesk?.email && cfg2.zendesk?.apiToken) {
15
+ return cfg2.zendesk;
16
+ }
17
+ // Try legacy zcli config directly
18
+ const legacy = getLegacyZendeskConfig();
19
+ if (legacy?.subdomain && legacy?.email && legacy?.api_token) {
20
+ return { subdomain: legacy.subdomain, email: legacy.email, apiToken: legacy.api_token };
21
+ }
22
+ throw new Error("Zendesk not configured. Use /zendy-config to set up credentials.\n" +
23
+ "Or set env: ZENDY_ZENDESK_SUBDOMAIN, ZENDY_ZENDESK_EMAIL, ZENDY_ZENDESK_API_TOKEN");
24
+ }
25
+ // ── HTTP helpers ───────────────────────────────────────────────────────
26
+ function authHeader(cfg) {
27
+ return "Basic " + Buffer.from(`${cfg.email}/token:${cfg.apiToken}`).toString("base64");
28
+ }
29
+ async function zendeskGet(path, signal) {
30
+ const cfg = resolveConfig();
31
+ const url = `https://${cfg.subdomain}.zendesk.com/api/v2${path}`;
32
+ const response = await fetch(url, {
33
+ headers: {
34
+ Authorization: authHeader(cfg),
35
+ Accept: "application/json",
36
+ "User-Agent": "zendy/1.0",
37
+ },
38
+ signal,
39
+ });
40
+ if (!response.ok) {
41
+ const body = await response.text().catch(() => "");
42
+ throw new Error(`Zendesk API error ${response.status} for ${path}: ${body.slice(0, 500)}`);
43
+ }
44
+ return response.json();
45
+ }
46
+ // ── Public API ─────────────────────────────────────────────────────────
47
+ export async function getTicket(ticketId, signal) {
48
+ return zendeskGet(`/tickets/${ticketId}.json`, signal);
49
+ }
50
+ export async function getTicketComments(ticketId, signal) {
51
+ return zendeskGet(`/tickets/${ticketId}/comments.json`, signal);
52
+ }
53
+ export async function getUser(userId, signal) {
54
+ return zendeskGet(`/users/${userId}.json`, signal);
55
+ }
56
+ export async function searchTickets(query, signal) {
57
+ return zendeskGet(`/search.json?query=${encodeURIComponent(query)}`, signal);
58
+ }
59
+ export async function getMe(signal) {
60
+ return zendeskGet("/users/me.json", signal);
61
+ }
62
+ export async function getTicketFull(ticketId, signal) {
63
+ const { ticket } = await getTicket(ticketId, signal);
64
+ let comments;
65
+ let requester;
66
+ let assignee;
67
+ try {
68
+ ({ comments } = await getTicketComments(ticketId, signal));
69
+ }
70
+ catch {
71
+ // comments optional
72
+ }
73
+ try {
74
+ if (ticket.requester_id) {
75
+ ({ user: requester } = await getUser(ticket.requester_id, signal));
76
+ }
77
+ }
78
+ catch {
79
+ // users optional
80
+ }
81
+ try {
82
+ if (ticket.assignee_id) {
83
+ ({ user: assignee } = await getUser(ticket.assignee_id, signal));
84
+ }
85
+ }
86
+ catch {
87
+ // users optional
88
+ }
89
+ return { ticket, comments, requester, assignee };
90
+ }
@@ -0,0 +1,6 @@
1
+ export declare function migrateLegacyConfig(): {
2
+ migrated: boolean;
3
+ source: string;
4
+ } | null;
5
+ export declare function getLegacyZendeskConfig(): Record<string, string> | null;
6
+ export declare function getLegacyKgEnv(): Record<string, string> | null;
@@ -0,0 +1,78 @@
1
+ // Migrate legacy zcli / zendesk-kg config into ~/.zendy/config.json.
2
+ // Non-destructive: writes to zendy config only if it doesn't already exist.
3
+ import { readFileSync, existsSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ import { homedir } from "node:os";
6
+ import { writeConfig, configExists } from "./store.js";
7
+ const LEGACY_ZCLI_PATH = join(homedir(), ".zendesk-cli", "config.json");
8
+ const LEGACY_KG_ENV_PATH = join(homedir(), ".zendesk-kg", ".env");
9
+ function parseLegacyKgEnv() {
10
+ const out = {};
11
+ try {
12
+ if (!existsSync(LEGACY_KG_ENV_PATH))
13
+ return out;
14
+ for (const line of readFileSync(LEGACY_KG_ENV_PATH, "utf-8").split(/\r?\n/)) {
15
+ const m = line.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)\s*$/);
16
+ if (m)
17
+ out[m[1]] = m[2].replace(/^['"]|['"]$/g, "");
18
+ }
19
+ }
20
+ catch { /* ignore */ }
21
+ return out;
22
+ }
23
+ function parseLegacyZcliConfig() {
24
+ try {
25
+ if (!existsSync(LEGACY_ZCLI_PATH))
26
+ return {};
27
+ const raw = readFileSync(LEGACY_ZCLI_PATH, "utf-8");
28
+ const j = JSON.parse(raw);
29
+ if (j && typeof j === "object")
30
+ return j;
31
+ }
32
+ catch { /* ignore */ }
33
+ return {};
34
+ }
35
+ export function migrateLegacyConfig() {
36
+ // Don't overwrite existing zendy config
37
+ if (configExists())
38
+ return null;
39
+ const zcli = parseLegacyZcliConfig();
40
+ const kg = parseLegacyKgEnv();
41
+ const hasZcli = zcli.subdomain && zcli.email && zcli.api_token;
42
+ const hasKg = !!kg["RETRIEVER_API_KEY"];
43
+ if (!hasZcli && !hasKg)
44
+ return null;
45
+ const config = {};
46
+ if (hasZcli) {
47
+ config.zendesk = {
48
+ subdomain: zcli.subdomain,
49
+ email: zcli.email,
50
+ apiToken: zcli.api_token,
51
+ };
52
+ }
53
+ if (hasKg) {
54
+ config.zendeskKg = {
55
+ apiUrl: kg["RETRIEVER_API_URL"] || undefined,
56
+ apiKey: kg["RETRIEVER_API_KEY"],
57
+ };
58
+ }
59
+ writeConfig(config);
60
+ const sources = [];
61
+ if (hasZcli)
62
+ sources.push("zcli (~/.zendesk-cli/config.json)");
63
+ if (hasKg)
64
+ sources.push("zendesk-kg (~/.zendesk-kg/.env)");
65
+ return { migrated: true, source: sources.join(", ") };
66
+ }
67
+ export function getLegacyZendeskConfig() {
68
+ const zcli = parseLegacyZcliConfig();
69
+ if (zcli.subdomain && zcli.email && zcli.api_token)
70
+ return zcli;
71
+ return null;
72
+ }
73
+ export function getLegacyKgEnv() {
74
+ const env = parseLegacyKgEnv();
75
+ if (env["RETRIEVER_API_KEY"])
76
+ return env;
77
+ return null;
78
+ }
@@ -0,0 +1,14 @@
1
+ export interface ZendyConfig {
2
+ zendesk?: ZendeskConfig;
3
+ zendeskKg?: ZendeskKgConfig;
4
+ }
5
+ export interface ZendeskConfig {
6
+ subdomain?: string;
7
+ email?: string;
8
+ apiToken?: string;
9
+ }
10
+ export interface ZendeskKgConfig {
11
+ apiUrl?: string;
12
+ apiKey?: string;
13
+ }
14
+ export declare const DEFAULT_KG_API_URL = "https://zendesk-ticket-retriever.vercel.app";
@@ -0,0 +1,2 @@
1
+ // Config schema and types for zendy configuration (~/.zendy/config.json).
2
+ export const DEFAULT_KG_API_URL = "https://zendesk-ticket-retriever.vercel.app";
@@ -0,0 +1,7 @@
1
+ import type { ZendyConfig, ZendeskConfig, ZendeskKgConfig } from "./schema.js";
2
+ export declare function getConfig(): ZendyConfig;
3
+ export declare function getZendeskConfig(): ZendeskConfig | undefined;
4
+ export declare function getZendeskKgConfig(): ZendeskKgConfig | undefined;
5
+ export declare function writeConfig(config: ZendyConfig): void;
6
+ export declare function configPath(): string;
7
+ export declare function configExists(): boolean;