mcp-pachca 0.3.0 → 0.5.1
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/dist/convert.d.ts +1 -0
- package/dist/convert.js +100 -0
- package/dist/convert.js.map +1 -0
- package/dist/dump.d.ts +1 -0
- package/dist/dump.js +237 -0
- package/dist/dump.js.map +1 -0
- package/dist/index.js +16 -4
- package/dist/index.js.map +1 -1
- package/dist/logger.js +3 -1
- package/dist/logger.js.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +13 -2
- package/dist/server.js.map +1 -1
- package/package.json +4 -1
- package/.mcp.json +0 -11
- package/src/auth.ts +0 -128
- package/src/client.ts +0 -130
- package/src/index.ts +0 -50
- package/src/logger.ts +0 -33
- package/src/server.ts +0 -51
- package/src/setup.ts +0 -201
- package/src/tools/handlers.ts +0 -212
- package/src/tools/schemas.ts +0 -195
- package/src/tools/types.ts +0 -127
- package/tsconfig.json +0 -19
package/src/client.ts
DELETED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import { logger } from "./logger.js";
|
|
2
|
-
import type { Session } from "./auth.js";
|
|
3
|
-
|
|
4
|
-
export class PachcaClient {
|
|
5
|
-
private readonly baseUrl = "https://app.pachca.com/api/v3";
|
|
6
|
-
private readonly session: Session;
|
|
7
|
-
|
|
8
|
-
constructor(session: Session) {
|
|
9
|
-
this.session = session;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
async get<T>(path: string, params?: Record<string, string>): Promise<T> {
|
|
13
|
-
const url = new URL(`${this.baseUrl}${path}`);
|
|
14
|
-
if (params) {
|
|
15
|
-
for (const [key, value] of Object.entries(params)) {
|
|
16
|
-
if (value !== undefined && value !== "") {
|
|
17
|
-
url.searchParams.set(key, value);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return (await this.request<T>("GET", url.toString())) as T;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async getWithArrayParams<T>(
|
|
25
|
-
path: string,
|
|
26
|
-
arrayParams: Record<string, (string | number)[]>,
|
|
27
|
-
params?: Record<string, string>,
|
|
28
|
-
): Promise<T> {
|
|
29
|
-
const url = new URL(`${this.baseUrl}${path}`);
|
|
30
|
-
if (params) {
|
|
31
|
-
for (const [key, value] of Object.entries(params)) {
|
|
32
|
-
if (value !== undefined && value !== "") {
|
|
33
|
-
url.searchParams.set(key, value);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
for (const [key, values] of Object.entries(arrayParams)) {
|
|
38
|
-
for (const v of values) {
|
|
39
|
-
url.searchParams.append(`${key}[]`, String(v));
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return (await this.request<T>("GET", url.toString())) as T;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async post<T>(path: string, body: unknown): Promise<T | undefined> {
|
|
46
|
-
return this.request<T>("POST", `${this.baseUrl}${path}`, body);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
private async request<T>(
|
|
50
|
-
method: string,
|
|
51
|
-
url: string,
|
|
52
|
-
body?: unknown,
|
|
53
|
-
): Promise<T | undefined> {
|
|
54
|
-
logger.debug("Pachca API request", { method, url });
|
|
55
|
-
|
|
56
|
-
const controller = new AbortController();
|
|
57
|
-
const timeout = setTimeout(() => controller.abort(), 30_000);
|
|
58
|
-
|
|
59
|
-
try {
|
|
60
|
-
const headers: Record<string, string> = {
|
|
61
|
-
Authorization: `Bearer ${this.session.workspaceJwt}`,
|
|
62
|
-
Accept: "application/json",
|
|
63
|
-
"user-id": String(this.session.profileId),
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
if (this.session.cookies.length > 0) {
|
|
67
|
-
headers["Cookie"] = this.session.cookies.join("; ");
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (body !== undefined) {
|
|
71
|
-
headers["Content-Type"] = "application/json; charset=utf-8";
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const response = await fetch(url, {
|
|
75
|
-
method,
|
|
76
|
-
headers,
|
|
77
|
-
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
78
|
-
signal: controller.signal,
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
if (response.status === 204) {
|
|
82
|
-
return undefined;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (response.status === 401) {
|
|
86
|
-
await response.text();
|
|
87
|
-
throw new Error("Session expired. Run: npx mcp-pachca --setup");
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (!response.ok) {
|
|
91
|
-
const rawBody = await response.text();
|
|
92
|
-
let errorBody: unknown;
|
|
93
|
-
try {
|
|
94
|
-
errorBody = JSON.parse(rawBody);
|
|
95
|
-
} catch {
|
|
96
|
-
errorBody = rawBody;
|
|
97
|
-
}
|
|
98
|
-
logger.error("Pachca API error", {
|
|
99
|
-
status: response.status,
|
|
100
|
-
body: errorBody,
|
|
101
|
-
});
|
|
102
|
-
throw new Error(
|
|
103
|
-
`Pachca API error (HTTP ${response.status}): ${JSON.stringify(errorBody)}`,
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const rawText = await response.text();
|
|
108
|
-
let data: T;
|
|
109
|
-
try {
|
|
110
|
-
data = JSON.parse(rawText) as T;
|
|
111
|
-
} catch {
|
|
112
|
-
throw new Error(
|
|
113
|
-
`Pachca API returned non-JSON response (HTTP ${response.status}): ${rawText.slice(0, 200)}`,
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
if (process.env["DEBUG"]) {
|
|
117
|
-
logger.debug("Pachca API response", { method, url, body: data });
|
|
118
|
-
}
|
|
119
|
-
return data;
|
|
120
|
-
} catch (err) {
|
|
121
|
-
if (err instanceof Error && err.name === "AbortError") {
|
|
122
|
-
logger.error("Pachca API request timeout", { url });
|
|
123
|
-
throw new Error(`Pachca API request timeout: ${url}`);
|
|
124
|
-
}
|
|
125
|
-
throw err;
|
|
126
|
-
} finally {
|
|
127
|
-
clearTimeout(timeout);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
-
import { createServer } from "./server.js";
|
|
5
|
-
import { loadSession } from "./auth.js";
|
|
6
|
-
import { logger } from "./logger.js";
|
|
7
|
-
import { runSetup } from "./setup.js";
|
|
8
|
-
|
|
9
|
-
async function main(): Promise<void> {
|
|
10
|
-
if (process.argv[2] === "--setup") {
|
|
11
|
-
await runSetup();
|
|
12
|
-
process.exit(0);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const session = loadSession();
|
|
16
|
-
if (!session) {
|
|
17
|
-
logger.error("No session found. Run: npx mcp-pachca --setup");
|
|
18
|
-
process.exit(1);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const server = createServer(session);
|
|
22
|
-
const transport = new StdioServerTransport();
|
|
23
|
-
|
|
24
|
-
const shutdown = async (signal: string): Promise<void> => {
|
|
25
|
-
logger.info(`Received ${signal}, shutting down`);
|
|
26
|
-
await server.close();
|
|
27
|
-
process.exit(0);
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
process.on("SIGINT", () =>
|
|
31
|
-
shutdown("SIGINT").catch((err) => {
|
|
32
|
-
logger.error("Error during shutdown", { error: String(err) });
|
|
33
|
-
process.exit(1);
|
|
34
|
-
}),
|
|
35
|
-
);
|
|
36
|
-
process.on("SIGTERM", () =>
|
|
37
|
-
shutdown("SIGTERM").catch((err) => {
|
|
38
|
-
logger.error("Error during shutdown", { error: String(err) });
|
|
39
|
-
process.exit(1);
|
|
40
|
-
}),
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
await server.connect(transport);
|
|
44
|
-
logger.info("MCP Pachca server started");
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
main().catch((err: unknown) => {
|
|
48
|
-
logger.error("Fatal error", err);
|
|
49
|
-
process.exit(1);
|
|
50
|
-
});
|
package/src/logger.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
export interface Logger {
|
|
2
|
-
info(message: string, data?: unknown): void;
|
|
3
|
-
warn(message: string, data?: unknown): void;
|
|
4
|
-
error(message: string, data?: unknown): void;
|
|
5
|
-
debug(message: string, data?: unknown): void;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
function formatLog(level: string, message: string, data?: unknown): string {
|
|
9
|
-
const entry: Record<string, unknown> = {
|
|
10
|
-
ts: new Date().toISOString(),
|
|
11
|
-
level,
|
|
12
|
-
msg: message,
|
|
13
|
-
};
|
|
14
|
-
if (data !== undefined) {
|
|
15
|
-
entry["data"] = data;
|
|
16
|
-
}
|
|
17
|
-
return JSON.stringify(entry);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export const logger: Logger = {
|
|
21
|
-
info(message, data) {
|
|
22
|
-
process.stderr.write(formatLog("info", message, data) + "\n");
|
|
23
|
-
},
|
|
24
|
-
warn(message, data) {
|
|
25
|
-
process.stderr.write(formatLog("warn", message, data) + "\n");
|
|
26
|
-
},
|
|
27
|
-
error(message, data) {
|
|
28
|
-
process.stderr.write(formatLog("error", message, data) + "\n");
|
|
29
|
-
},
|
|
30
|
-
debug(message, data) {
|
|
31
|
-
process.stderr.write(formatLog("debug", message, data) + "\n");
|
|
32
|
-
},
|
|
33
|
-
};
|
package/src/server.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
-
import {
|
|
3
|
-
ListToolsRequestSchema,
|
|
4
|
-
CallToolRequestSchema,
|
|
5
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
-
import type { Session } from "./auth.js";
|
|
7
|
-
import { PachcaClient } from "./client.js";
|
|
8
|
-
import { toolDefinitions } from "./tools/schemas.js";
|
|
9
|
-
import { handlerRegistry } from "./tools/handlers.js";
|
|
10
|
-
import { logger } from "./logger.js";
|
|
11
|
-
|
|
12
|
-
export function createServer(session: Session): Server {
|
|
13
|
-
const client = new PachcaClient(session);
|
|
14
|
-
|
|
15
|
-
const server = new Server(
|
|
16
|
-
{ name: "mcp-pachca", version: "0.3.0" },
|
|
17
|
-
{ capabilities: { tools: {} } },
|
|
18
|
-
);
|
|
19
|
-
|
|
20
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
21
|
-
tools: toolDefinitions,
|
|
22
|
-
}));
|
|
23
|
-
|
|
24
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
25
|
-
const { name, arguments: args } = request.params;
|
|
26
|
-
const handler = handlerRegistry[name];
|
|
27
|
-
|
|
28
|
-
if (!handler) {
|
|
29
|
-
return {
|
|
30
|
-
content: [{ type: "text" as const, text: `Unknown tool: ${name}` }],
|
|
31
|
-
isError: true,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
const result = await handler(client, (args ?? {}) as Record<string, unknown>);
|
|
37
|
-
const text = JSON.stringify(result, null, 2) ?? "null";
|
|
38
|
-
return { content: [{ type: "text" as const, text }] };
|
|
39
|
-
} catch (err) {
|
|
40
|
-
logger.error("Tool call failed", { tool: name, error: String(err) });
|
|
41
|
-
const message =
|
|
42
|
-
err instanceof Error ? err.message : "Unknown error during tool call";
|
|
43
|
-
return {
|
|
44
|
-
content: [{ type: "text" as const, text: message }],
|
|
45
|
-
isError: true,
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
return server;
|
|
51
|
-
}
|
package/src/setup.ts
DELETED
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import * as os from "node:os";
|
|
4
|
-
import * as rl from "node:readline/promises";
|
|
5
|
-
import { requestOTP, performLogin, saveSession } from "./auth.js";
|
|
6
|
-
|
|
7
|
-
const CLIENTS = ["Claude Code", "Cursor"] as const;
|
|
8
|
-
type Client = (typeof CLIENTS)[number];
|
|
9
|
-
|
|
10
|
-
const SCOPES = ["Project", "Global"] as const;
|
|
11
|
-
type Scope = (typeof SCOPES)[number];
|
|
12
|
-
|
|
13
|
-
function stderr(msg: string): void {
|
|
14
|
-
process.stderr.write(msg);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function stderrln(msg: string = ""): void {
|
|
18
|
-
process.stderr.write(msg + "\n");
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// --- Prompt helpers ---
|
|
22
|
-
|
|
23
|
-
/** Collect all lines from a non-TTY stdin upfront (avoids readline buffering issues). */
|
|
24
|
-
function readAllLines(): Promise<string[]> {
|
|
25
|
-
return new Promise((resolve, reject) => {
|
|
26
|
-
const lines: string[] = [];
|
|
27
|
-
const iface = rl.createInterface({ input: process.stdin });
|
|
28
|
-
iface.on("line", (line) => lines.push(line));
|
|
29
|
-
iface.on("close", () => resolve(lines));
|
|
30
|
-
iface.on("error", (err) => {
|
|
31
|
-
iface.close();
|
|
32
|
-
reject(err);
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** Simple line-based prompter backed by a pre-read array (non-TTY). */
|
|
38
|
-
function makePipePrompter(lines: string[]) {
|
|
39
|
-
let idx = 0;
|
|
40
|
-
return {
|
|
41
|
-
ask(question: string): string {
|
|
42
|
-
stderr(question);
|
|
43
|
-
if (idx >= lines.length) {
|
|
44
|
-
stderrln("\nError: not enough input lines.");
|
|
45
|
-
process.exit(1);
|
|
46
|
-
}
|
|
47
|
-
const answer = lines[idx++]!.trim();
|
|
48
|
-
stderrln(answer);
|
|
49
|
-
return answer;
|
|
50
|
-
},
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** Interactive TTY prompter using readline/promises. */
|
|
55
|
-
function makeTTYPrompter() {
|
|
56
|
-
const iface = rl.createInterface({ input: process.stdin, output: process.stderr });
|
|
57
|
-
return {
|
|
58
|
-
async ask(question: string): Promise<string> {
|
|
59
|
-
const answer = await iface.question(question);
|
|
60
|
-
return answer.trim();
|
|
61
|
-
},
|
|
62
|
-
close() {
|
|
63
|
-
iface.close();
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// --- Choice helper ---
|
|
69
|
-
|
|
70
|
-
async function askChoice<T extends string>(
|
|
71
|
-
ask: (q: string) => string | Promise<string>,
|
|
72
|
-
question: string,
|
|
73
|
-
choices: readonly T[],
|
|
74
|
-
retry = true,
|
|
75
|
-
): Promise<T> {
|
|
76
|
-
stderrln(question);
|
|
77
|
-
for (let i = 0; i < choices.length; i++) {
|
|
78
|
-
stderrln(` ${i + 1}) ${choices[i]}`);
|
|
79
|
-
}
|
|
80
|
-
while (true) {
|
|
81
|
-
const raw = await ask("Enter choice: ");
|
|
82
|
-
const n = parseInt(raw, 10);
|
|
83
|
-
if (n >= 1 && n <= choices.length) {
|
|
84
|
-
return choices[n - 1]!;
|
|
85
|
-
}
|
|
86
|
-
if (!retry) {
|
|
87
|
-
stderrln(`Error: invalid choice "${raw}". Expected a number between 1 and ${choices.length}.`);
|
|
88
|
-
process.exit(1);
|
|
89
|
-
}
|
|
90
|
-
stderrln(`Please enter a number between 1 and ${choices.length}.`);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// --- Config helpers ---
|
|
95
|
-
|
|
96
|
-
function resolveConfigPath(client: Client, scope: Scope): string {
|
|
97
|
-
if (client === "Claude Code") {
|
|
98
|
-
return scope === "Project"
|
|
99
|
-
? path.join(process.cwd(), ".mcp.json")
|
|
100
|
-
: path.join(os.homedir(), ".mcp.json");
|
|
101
|
-
}
|
|
102
|
-
// Cursor
|
|
103
|
-
return scope === "Project"
|
|
104
|
-
? path.join(process.cwd(), ".cursor", "mcp.json")
|
|
105
|
-
: path.join(os.homedir(), ".cursor", "mcp.json");
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function buildEntry(): Record<string, unknown> {
|
|
109
|
-
const entry: Record<string, unknown> = {
|
|
110
|
-
type: "stdio",
|
|
111
|
-
command: "npx",
|
|
112
|
-
args: ["-y", "mcp-pachca@latest"],
|
|
113
|
-
};
|
|
114
|
-
if (process.env["DEBUG"]) {
|
|
115
|
-
entry["env"] = { DEBUG: "1" };
|
|
116
|
-
}
|
|
117
|
-
return entry;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// --- Main ---
|
|
121
|
-
|
|
122
|
-
export async function runSetup(): Promise<void> {
|
|
123
|
-
stderrln("mcp-pachca setup wizard\n");
|
|
124
|
-
|
|
125
|
-
let client: Client;
|
|
126
|
-
let scope: Scope;
|
|
127
|
-
let email: string;
|
|
128
|
-
let code: string;
|
|
129
|
-
|
|
130
|
-
if (!process.stdin.isTTY) {
|
|
131
|
-
stderrln("Error: --setup requires an interactive terminal (OTP code is sent via email).");
|
|
132
|
-
process.exit(1);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const prompter = makeTTYPrompter();
|
|
136
|
-
try {
|
|
137
|
-
client = await askChoice(prompter.ask, "Select your MCP client:", CLIENTS);
|
|
138
|
-
scope = await askChoice(prompter.ask, "Select config scope:", SCOPES);
|
|
139
|
-
|
|
140
|
-
email = await prompter.ask("Enter your Pachca email: ");
|
|
141
|
-
if (!email || !email.includes("@")) {
|
|
142
|
-
stderrln("Error: invalid email address.");
|
|
143
|
-
process.exit(1);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
stderrln("\nRequesting login code...");
|
|
147
|
-
await requestOTP(email);
|
|
148
|
-
stderrln("Check your email for the 6-digit code.");
|
|
149
|
-
|
|
150
|
-
code = await prompter.ask("Enter the code: ");
|
|
151
|
-
} finally {
|
|
152
|
-
prompter.close();
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (!code || !/^\d{6}$/.test(code)) {
|
|
156
|
-
stderrln("Error: code must be exactly 6 digits.");
|
|
157
|
-
process.exit(1);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
stderrln("Logging in...");
|
|
161
|
-
let session;
|
|
162
|
-
try {
|
|
163
|
-
session = await performLogin(email, code);
|
|
164
|
-
} catch (err) {
|
|
165
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
166
|
-
stderrln(`\nLogin failed: ${msg}`);
|
|
167
|
-
process.exit(1);
|
|
168
|
-
}
|
|
169
|
-
saveSession(session);
|
|
170
|
-
stderrln(`Session saved to ~/.config/mcp-pachca/session.json`);
|
|
171
|
-
|
|
172
|
-
// Write MCP config
|
|
173
|
-
const configPath = resolveConfigPath(client, scope);
|
|
174
|
-
const dir = path.dirname(configPath);
|
|
175
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
176
|
-
|
|
177
|
-
let config: Record<string, unknown>;
|
|
178
|
-
if (fs.existsSync(configPath)) {
|
|
179
|
-
const raw = fs.readFileSync(configPath, "utf8");
|
|
180
|
-
try {
|
|
181
|
-
config = JSON.parse(raw) as Record<string, unknown>;
|
|
182
|
-
} catch {
|
|
183
|
-
stderrln(
|
|
184
|
-
`Error: ${configPath} contains malformed JSON. Fix it manually before re-running setup.`,
|
|
185
|
-
);
|
|
186
|
-
process.exit(1);
|
|
187
|
-
}
|
|
188
|
-
} else {
|
|
189
|
-
config = {};
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const raw = config["mcpServers"];
|
|
193
|
-
const servers = (raw !== null && typeof raw === "object" && !Array.isArray(raw) ? raw : {}) as Record<string, unknown>;
|
|
194
|
-
servers["pachca"] = buildEntry();
|
|
195
|
-
config["mcpServers"] = servers;
|
|
196
|
-
|
|
197
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
198
|
-
|
|
199
|
-
stderrln(`\nConfig written to ${configPath}`);
|
|
200
|
-
stderrln("Restart your MCP client to apply changes.");
|
|
201
|
-
}
|
package/src/tools/handlers.ts
DELETED
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
import type { PachcaClient } from "../client.js";
|
|
3
|
-
import type {
|
|
4
|
-
PachcaUser,
|
|
5
|
-
PachcaChat,
|
|
6
|
-
PachcaMessage,
|
|
7
|
-
PachcaFolder,
|
|
8
|
-
PachcaProfile,
|
|
9
|
-
PachcaPresence,
|
|
10
|
-
PachcaSearchResult,
|
|
11
|
-
PachcaPaginate,
|
|
12
|
-
} from "./types.js";
|
|
13
|
-
|
|
14
|
-
type HandlerFn = (
|
|
15
|
-
client: PachcaClient,
|
|
16
|
-
args: Record<string, unknown>,
|
|
17
|
-
) => Promise<unknown>;
|
|
18
|
-
|
|
19
|
-
async function handleSearchUsers(
|
|
20
|
-
client: PachcaClient,
|
|
21
|
-
args: Record<string, unknown>,
|
|
22
|
-
): Promise<unknown> {
|
|
23
|
-
const params: Record<string, string> = {
|
|
24
|
-
type: "user",
|
|
25
|
-
query: String(args["query"]),
|
|
26
|
-
};
|
|
27
|
-
if (args["cursor"] !== undefined) params["cursor"] = String(args["cursor"]);
|
|
28
|
-
return client.get<PachcaSearchResult<PachcaUser>>("/search", params);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async function handleGetUser(
|
|
32
|
-
client: PachcaClient,
|
|
33
|
-
args: Record<string, unknown>,
|
|
34
|
-
): Promise<unknown> {
|
|
35
|
-
const data = await client.getWithArrayParams<{ data: PachcaUser[] }>(
|
|
36
|
-
"/users",
|
|
37
|
-
{ ids: [Number(args["id"])] },
|
|
38
|
-
);
|
|
39
|
-
if (!data?.data?.length) throw new Error(`User ${args["id"]} not found`);
|
|
40
|
-
return data.data[0];
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async function handleListChats(
|
|
44
|
-
client: PachcaClient,
|
|
45
|
-
args: Record<string, unknown>,
|
|
46
|
-
): Promise<unknown> {
|
|
47
|
-
const res = await client.get<{ data: PachcaFolder[] }>("/menu/folders");
|
|
48
|
-
const folders = res?.data ?? [];
|
|
49
|
-
|
|
50
|
-
const folderKind = args["folder"] as string | undefined;
|
|
51
|
-
if (!folderKind) {
|
|
52
|
-
return {
|
|
53
|
-
folders: folders.map((f) => ({
|
|
54
|
-
id: f.id,
|
|
55
|
-
title: f.title,
|
|
56
|
-
kind: f.kind,
|
|
57
|
-
})),
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const folder = folders.find((f) => f.kind === folderKind);
|
|
62
|
-
if (!folder) {
|
|
63
|
-
throw new Error(`Folder "${folderKind}" not found. Available: ${folders.map((f) => f.kind).join(", ")}`);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const params: Record<string, string> = {};
|
|
67
|
-
if (args["cursor"] !== undefined) params["cursor"] = String(args["cursor"]);
|
|
68
|
-
|
|
69
|
-
return client.get<{ records: PachcaChat[]; paginate: PachcaPaginate }>(
|
|
70
|
-
`/menu/folders/${folder.id}/chats`,
|
|
71
|
-
params,
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
async function handleGetChat(
|
|
76
|
-
client: PachcaClient,
|
|
77
|
-
args: Record<string, unknown>,
|
|
78
|
-
): Promise<unknown> {
|
|
79
|
-
const data = await client.getWithArrayParams<{ records: PachcaChat[] }>(
|
|
80
|
-
"/chats",
|
|
81
|
-
{ ids: [Number(args["id"])] },
|
|
82
|
-
);
|
|
83
|
-
if (!data?.records?.length) throw new Error(`Chat ${args["id"]} not found`);
|
|
84
|
-
return data.records[0];
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
async function handleGetChatMembers(
|
|
88
|
-
client: PachcaClient,
|
|
89
|
-
args: Record<string, unknown>,
|
|
90
|
-
): Promise<unknown> {
|
|
91
|
-
const chatId = Number(args["id"]);
|
|
92
|
-
const params: Record<string, string> = {};
|
|
93
|
-
if (args["limit"] !== undefined) params["limit"] = String(args["limit"]);
|
|
94
|
-
if (args["cursor"] !== undefined) params["cursor"] = String(args["cursor"]);
|
|
95
|
-
return client.get<{
|
|
96
|
-
users: PachcaUser[];
|
|
97
|
-
total_users: number;
|
|
98
|
-
paginate: PachcaPaginate;
|
|
99
|
-
}>(`/chats/${chatId}/users`, params);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
async function handleListMessages(
|
|
103
|
-
client: PachcaClient,
|
|
104
|
-
args: Record<string, unknown>,
|
|
105
|
-
): Promise<unknown> {
|
|
106
|
-
const params: Record<string, string> = {
|
|
107
|
-
chat_id: String(args["chat_id"]),
|
|
108
|
-
};
|
|
109
|
-
if (args["per"] !== undefined) params["per"] = String(args["per"]);
|
|
110
|
-
if (args["message_id"] !== undefined) params["message_id"] = String(args["message_id"]);
|
|
111
|
-
if (args["direction"] !== undefined) {
|
|
112
|
-
if (args["message_id"] === undefined) {
|
|
113
|
-
throw new Error("direction requires message_id to be set");
|
|
114
|
-
}
|
|
115
|
-
params["direction"] = String(args["direction"]);
|
|
116
|
-
}
|
|
117
|
-
return client.get<PachcaMessage[]>("/messages", params);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
async function handleGetMessage(
|
|
121
|
-
client: PachcaClient,
|
|
122
|
-
args: Record<string, unknown>,
|
|
123
|
-
): Promise<unknown> {
|
|
124
|
-
const data = await client.getWithArrayParams<PachcaMessage[]>("/messages", {
|
|
125
|
-
ids: [Number(args["id"])],
|
|
126
|
-
});
|
|
127
|
-
if (!data?.length) throw new Error(`Message ${args["id"]} not found`);
|
|
128
|
-
return data[0];
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
async function handleSendMessage(
|
|
132
|
-
client: PachcaClient,
|
|
133
|
-
args: Record<string, unknown>,
|
|
134
|
-
): Promise<unknown> {
|
|
135
|
-
const message: Record<string, unknown> = {
|
|
136
|
-
uuid: randomUUID(),
|
|
137
|
-
content: String(args["text"]),
|
|
138
|
-
markup: [],
|
|
139
|
-
kind: 0,
|
|
140
|
-
files: [],
|
|
141
|
-
skip_invite_mentions: false,
|
|
142
|
-
};
|
|
143
|
-
if (args["parent_message_id"] !== undefined) {
|
|
144
|
-
message["parent_message_id"] = Number(args["parent_message_id"]);
|
|
145
|
-
}
|
|
146
|
-
const body = {
|
|
147
|
-
chat_id: Number(args["chat_id"]),
|
|
148
|
-
message,
|
|
149
|
-
};
|
|
150
|
-
const result = await client.post<PachcaMessage>("/messages", body);
|
|
151
|
-
return result ?? { success: true, uuid: message.uuid, chat_id: body.chat_id };
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
async function handleSearch(
|
|
155
|
-
client: PachcaClient,
|
|
156
|
-
args: Record<string, unknown>,
|
|
157
|
-
): Promise<unknown> {
|
|
158
|
-
const params: Record<string, string> = {
|
|
159
|
-
type: "message",
|
|
160
|
-
query: String(args["query"]),
|
|
161
|
-
};
|
|
162
|
-
if (args["cursor"] !== undefined) params["cursor"] = String(args["cursor"]);
|
|
163
|
-
return client.get<PachcaSearchResult<PachcaMessage>>("/search", params);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
async function handleGetProfile(client: PachcaClient): Promise<unknown> {
|
|
167
|
-
return client.get<PachcaProfile>("/profile");
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
async function handleGetUnread(client: PachcaClient): Promise<unknown> {
|
|
171
|
-
return client.get<number[]>("/chats/unread_ids");
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
async function handleGetPresence(
|
|
175
|
-
client: PachcaClient,
|
|
176
|
-
args: Record<string, unknown>,
|
|
177
|
-
): Promise<unknown> {
|
|
178
|
-
const rawIds = args["ids"];
|
|
179
|
-
if (!Array.isArray(rawIds) || rawIds.length === 0) {
|
|
180
|
-
throw new Error("ids must be a non-empty array of numbers");
|
|
181
|
-
}
|
|
182
|
-
const ids = rawIds.map((id) => Number(id));
|
|
183
|
-
return client.getWithArrayParams<PachcaPresence[]>("/friends/presence", {
|
|
184
|
-
ids,
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
async function handleGetPersonalChat(
|
|
189
|
-
client: PachcaClient,
|
|
190
|
-
args: Record<string, unknown>,
|
|
191
|
-
): Promise<unknown> {
|
|
192
|
-
return client.getWithArrayParams<Record<string, number>>(
|
|
193
|
-
"/users/personal_chats",
|
|
194
|
-
{ ids: [Number(args["id"])] },
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
export const handlerRegistry: Record<string, HandlerFn> = {
|
|
199
|
-
pachca_search_users: handleSearchUsers,
|
|
200
|
-
pachca_get_user: handleGetUser,
|
|
201
|
-
pachca_list_chats: handleListChats,
|
|
202
|
-
pachca_get_chat: handleGetChat,
|
|
203
|
-
pachca_get_chat_members: handleGetChatMembers,
|
|
204
|
-
pachca_list_messages: handleListMessages,
|
|
205
|
-
pachca_get_message: handleGetMessage,
|
|
206
|
-
pachca_send_message: handleSendMessage,
|
|
207
|
-
pachca_search: handleSearch,
|
|
208
|
-
pachca_get_profile: handleGetProfile,
|
|
209
|
-
pachca_get_unread: handleGetUnread,
|
|
210
|
-
pachca_get_presence: handleGetPresence,
|
|
211
|
-
pachca_get_personal_chat: handleGetPersonalChat,
|
|
212
|
-
};
|