@tplog/pi-zendy 0.3.0 → 0.3.2
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 +1 -2
- package/extensions/commands.ts +20 -65
- package/extensions/zendy.ts +15 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ Powered by [pi](https://pi.dev).
|
|
|
11
11
|
zendy is a single pi extension that provides:
|
|
12
12
|
|
|
13
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
|
|
14
|
+
- **Slash Commands** — `/zendy-config` to set up credentials, `/zendy-status` to check connectivity.
|
|
15
15
|
- **Session Safety** — Automatic workspace isolation and cleanup for source code analysis.
|
|
16
16
|
|
|
17
17
|
Typical workflow:
|
|
@@ -60,7 +60,6 @@ from legacy `zcli` and `zendesk-kg` config files if they exist.
|
|
|
60
60
|
|---------|---------|
|
|
61
61
|
| `/zendy-config` | Configure Zendesk and KG credentials |
|
|
62
62
|
| `/zendy-status` | Check connectivity to all services |
|
|
63
|
-
| `/zendy-cleanup` | Wipe source clones and orphan session dirs |
|
|
64
63
|
|
|
65
64
|
## Tools
|
|
66
65
|
|
package/extensions/commands.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Zendy slash commands — /zendy-config, /zendy-status
|
|
1
|
+
// Zendy slash commands — /zendy-config, /zendy-status.
|
|
2
2
|
// Loaded by jiti as part of the zendy extension.
|
|
3
3
|
|
|
4
4
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
@@ -10,8 +10,8 @@ import * as kg from "../dist/clients/zendesk-kg.js";
|
|
|
10
10
|
|
|
11
11
|
async function configCommand(_args: string, ctx: {
|
|
12
12
|
ui: {
|
|
13
|
-
select: (
|
|
14
|
-
input: (
|
|
13
|
+
select: (prompt: string, options: string[]) => Promise<string | undefined>;
|
|
14
|
+
input: (prompt: string, placeholder?: string) => Promise<string | undefined>;
|
|
15
15
|
notify: (msg: string, level: string) => void;
|
|
16
16
|
};
|
|
17
17
|
}): Promise<void> {
|
|
@@ -20,26 +20,19 @@ async function configCommand(_args: string, ctx: {
|
|
|
20
20
|
ctx.ui.notify(`[zendy-config] Auto-imported credentials from ${migrated.source}`, "info");
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
const action = await ctx.ui.select(
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
{ label: "View current config", value: "view" },
|
|
30
|
-
],
|
|
31
|
-
);
|
|
23
|
+
const action = await ctx.ui.select("Zendy Config — what would you like to configure?", [
|
|
24
|
+
"Zendesk credentials",
|
|
25
|
+
"Knowledge Graph credentials",
|
|
26
|
+
"View current config",
|
|
27
|
+
]);
|
|
28
|
+
if (!action) return;
|
|
32
29
|
|
|
33
30
|
const config: ZendyConfig = getConfig();
|
|
34
31
|
|
|
35
|
-
if (action === "
|
|
36
|
-
const subdomain = await ctx.ui.input("Zendesk
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const email = await ctx.ui.input("Zendesk Config", "Zendesk email address", {
|
|
40
|
-
placeholder: config.zendesk?.email ?? "",
|
|
41
|
-
});
|
|
42
|
-
const apiToken = await ctx.ui.input("Zendesk Config", "Zendesk API token", { password: true });
|
|
32
|
+
if (action === "Zendesk credentials") {
|
|
33
|
+
const subdomain = await ctx.ui.input("Zendesk subdomain (e.g., dify)", config.zendesk?.subdomain ?? "dify");
|
|
34
|
+
const email = await ctx.ui.input("Zendesk email address", config.zendesk?.email ?? "");
|
|
35
|
+
const apiToken = await ctx.ui.input("Zendesk API token", "");
|
|
43
36
|
config.zendesk = {
|
|
44
37
|
...config.zendesk,
|
|
45
38
|
subdomain: subdomain || config.zendesk?.subdomain,
|
|
@@ -48,11 +41,12 @@ async function configCommand(_args: string, ctx: {
|
|
|
48
41
|
};
|
|
49
42
|
writeConfig(config);
|
|
50
43
|
ctx.ui.notify("[zendy-config] Zendesk credentials saved.", "info");
|
|
51
|
-
} else if (action === "
|
|
52
|
-
const apiUrl = await ctx.ui.input(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
44
|
+
} else if (action === "Knowledge Graph credentials") {
|
|
45
|
+
const apiUrl = await ctx.ui.input(
|
|
46
|
+
"Knowledge Graph API URL (leave empty for default)",
|
|
47
|
+
config.zendeskKg?.apiUrl ?? "https://zendesk-ticket-retriever.vercel.app",
|
|
48
|
+
);
|
|
49
|
+
const apiKey = await ctx.ui.input("Knowledge Graph API key", "");
|
|
56
50
|
config.zendeskKg = {
|
|
57
51
|
...config.zendeskKg,
|
|
58
52
|
apiUrl: apiUrl || config.zendeskKg?.apiUrl,
|
|
@@ -60,7 +54,7 @@ async function configCommand(_args: string, ctx: {
|
|
|
60
54
|
};
|
|
61
55
|
writeConfig(config);
|
|
62
56
|
ctx.ui.notify("[zendy-config] Knowledge Graph credentials saved.", "info");
|
|
63
|
-
} else if (action === "
|
|
57
|
+
} else if (action === "View current config") {
|
|
64
58
|
const c = getConfig();
|
|
65
59
|
const lines: string[] = [];
|
|
66
60
|
lines.push(`Config file: ${configPath()}`);
|
|
@@ -113,41 +107,6 @@ async function statusCommand(_args: string, ctx: {
|
|
|
113
107
|
ctx.ui.notify(`Zendy Status:\n${lines.join("\n")}`, hasIssue ? "error" : "info");
|
|
114
108
|
}
|
|
115
109
|
|
|
116
|
-
async function cleanupCommand(_args: string, ctx: {
|
|
117
|
-
ui: { notify: (msg: string, level: "info" | "error" | "warn") => void };
|
|
118
|
-
}): Promise<void> {
|
|
119
|
-
const { readdirSync, rmSync } = await import("node:fs");
|
|
120
|
-
const { join } = await import("node:path");
|
|
121
|
-
const base = "/tmp";
|
|
122
|
-
const prefixes = ["dify-", "zendy-session-"];
|
|
123
|
-
const sessionDir = process.env["ZENDY_SRC_DIR"];
|
|
124
|
-
const removed: string[] = [];
|
|
125
|
-
|
|
126
|
-
try {
|
|
127
|
-
const entries = readdirSync(base, { withFileTypes: true });
|
|
128
|
-
for (const e of entries) {
|
|
129
|
-
if (!e.isDirectory()) continue;
|
|
130
|
-
if (!prefixes.some((p) => e.name.startsWith(p))) continue;
|
|
131
|
-
const full = join(base, e.name);
|
|
132
|
-
if (full === sessionDir) continue;
|
|
133
|
-
try {
|
|
134
|
-
rmSync(full, { recursive: true, force: true });
|
|
135
|
-
removed.push(full);
|
|
136
|
-
} catch {
|
|
137
|
-
/* ignore */
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
ctx.ui.notify(
|
|
141
|
-
removed.length === 0
|
|
142
|
-
? "[zendy-cleanup] Nothing to remove."
|
|
143
|
-
: `[zendy-cleanup] Removed ${removed.length} dir(s):\n${removed.join("\n")}`,
|
|
144
|
-
"info",
|
|
145
|
-
);
|
|
146
|
-
} catch (e) {
|
|
147
|
-
ctx.ui.notify(`[zendy-cleanup] Error: ${(e as Error).message}`, "error");
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
110
|
export function registerAllCommands(pi: ExtensionAPI): void {
|
|
152
111
|
pi.registerCommand("zendy-config", {
|
|
153
112
|
description: "Configure Zendesk and Knowledge Graph credentials",
|
|
@@ -157,8 +116,4 @@ export function registerAllCommands(pi: ExtensionAPI): void {
|
|
|
157
116
|
description: "Check Zendy connectivity: Zendesk, KG, Helm Watchdog",
|
|
158
117
|
handler: statusCommand,
|
|
159
118
|
});
|
|
160
|
-
pi.registerCommand("zendy-cleanup", {
|
|
161
|
-
description: "Wipe Dify source clones and orphan session dirs from /tmp",
|
|
162
|
-
handler: cleanupCommand,
|
|
163
|
-
});
|
|
164
119
|
}
|
package/extensions/zendy.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Provides:
|
|
5
5
|
* - Tools (LLM-callable): zendy_ticket_get, zendy_ticket_search,
|
|
6
6
|
* zendy_helm_get, zendy_kg_search, zendy_source_status
|
|
7
|
-
* - Slash commands: /zendy-config, /zendy-status
|
|
7
|
+
* - Slash commands: /zendy-config, /zendy-status
|
|
8
8
|
* - Session lifecycle: workspace dir + cleanup + orphan sweep
|
|
9
9
|
* - Custom header on session start
|
|
10
10
|
*
|
|
@@ -107,6 +107,20 @@ export default function (pi: ExtensionAPI) {
|
|
|
107
107
|
if (cleanedUp) return;
|
|
108
108
|
cleanedUp = true;
|
|
109
109
|
safeRmrf(sessionDir);
|
|
110
|
+
|
|
111
|
+
// Do not rely on the human remembering manual cleanup: wipe standalone
|
|
112
|
+
// source clones too when pi exits. The current session dir was already
|
|
113
|
+
// removed above; this covers clones created directly under /tmp/dify-*.
|
|
114
|
+
try {
|
|
115
|
+
const entries = readdirSync(BASE_DIR, { withFileTypes: true });
|
|
116
|
+
for (const e of entries) {
|
|
117
|
+
if (!e.isDirectory()) continue;
|
|
118
|
+
if (!WIPE_PREFIXES.some((p) => e.name.startsWith(p))) continue;
|
|
119
|
+
safeRmrf(join(BASE_DIR, e.name));
|
|
120
|
+
}
|
|
121
|
+
} catch {
|
|
122
|
+
// non-fatal
|
|
123
|
+
}
|
|
110
124
|
}
|
|
111
125
|
|
|
112
126
|
process.on("exit", cleanupSession);
|