@openape/apes 0.2.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.
@@ -0,0 +1,333 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ apiFetch,
4
+ getAuthToken,
5
+ getGrantsEndpoint,
6
+ getIdpUrl,
7
+ getRequesterIdentity
8
+ } from "./chunk-2JEBWXMI.js";
9
+
10
+ // src/commands/mcp/server.ts
11
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
12
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
13
+ import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
14
+ import { createServer } from "http";
15
+ import consola from "consola";
16
+
17
+ // src/commands/mcp/tools.ts
18
+ import { hostname } from "os";
19
+ import {
20
+ createShapesGrant,
21
+ fetchGrantToken,
22
+ loadAdapter as loadAdapter2,
23
+ resolveCommand,
24
+ verifyAndExecute
25
+ } from "@openape/shapes";
26
+ import { z } from "zod";
27
+
28
+ // src/commands/mcp/adapter-tools.ts
29
+ import { readdirSync } from "fs";
30
+ import { homedir } from "os";
31
+ import { join } from "path";
32
+ import { loadAdapter } from "@openape/shapes";
33
+ function operationToInputSchema(op) {
34
+ const properties = {};
35
+ const required = [];
36
+ if (op.positionals) {
37
+ for (const pos of op.positionals) {
38
+ properties[pos] = { type: "string", description: `Positional argument: ${pos}` };
39
+ required.push(pos);
40
+ }
41
+ }
42
+ if (op.required_options) {
43
+ for (const opt of op.required_options) {
44
+ const name = opt.replace(/^--/, "");
45
+ properties[name] = { type: "string", description: `Required option: ${opt}` };
46
+ required.push(name);
47
+ }
48
+ }
49
+ return {
50
+ type: "object",
51
+ properties,
52
+ required: required.length > 0 ? required : void 0
53
+ };
54
+ }
55
+ function scanAdapterDir(dir) {
56
+ try {
57
+ return readdirSync(dir).filter((f) => f.endsWith(".toml")).map((f) => f.replace(".toml", ""));
58
+ } catch {
59
+ return [];
60
+ }
61
+ }
62
+ function loadAdapterTools() {
63
+ const tools = [];
64
+ const seen = /* @__PURE__ */ new Set();
65
+ const adapterDirs = [
66
+ join(process.cwd(), ".openape", "shapes", "adapters"),
67
+ join(homedir(), ".openape", "shapes", "adapters"),
68
+ "/etc/openape/shapes/adapters"
69
+ ];
70
+ for (const dir of adapterDirs) {
71
+ for (const id of scanAdapterDir(dir)) {
72
+ if (seen.has(id))
73
+ continue;
74
+ seen.add(id);
75
+ try {
76
+ const loaded = loadAdapter(id);
77
+ for (const op of loaded.adapter.operations) {
78
+ tools.push({
79
+ name: `apes.run.${id}.${op.id}`,
80
+ description: op.display || `${id}: ${op.id}`,
81
+ inputSchema: operationToInputSchema(op),
82
+ adapterId: id,
83
+ operationId: op.id
84
+ });
85
+ }
86
+ } catch {
87
+ }
88
+ }
89
+ }
90
+ return tools;
91
+ }
92
+
93
+ // src/commands/mcp/tools.ts
94
+ function registerStaticTools(server) {
95
+ server.registerTool("apes.grants.list", {
96
+ description: "List grants",
97
+ inputSchema: {
98
+ status: z.string().optional().describe("Filter by status: pending, approved, denied, revoked, used"),
99
+ limit: z.string().optional().describe("Max results")
100
+ }
101
+ }, async ({ status, limit }) => {
102
+ const idp = getIdpUrl();
103
+ if (!idp)
104
+ return { content: [{ type: "text", text: "Not configured. Run `apes login` first." }] };
105
+ const grantsUrl = await getGrantsEndpoint(idp);
106
+ const params = new URLSearchParams();
107
+ if (status)
108
+ params.set("status", status);
109
+ if (limit)
110
+ params.set("limit", limit);
111
+ const query = params.toString() ? `?${params.toString()}` : "";
112
+ const response = await apiFetch(`${grantsUrl}${query}`);
113
+ return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
114
+ });
115
+ server.registerTool("apes.grants.request", {
116
+ description: "Request a grant for a command",
117
+ inputSchema: {
118
+ command: z.string().describe("Command to request permission for"),
119
+ audience: z.string().describe("Service identifier (e.g. escapes, proxy, shapes)"),
120
+ approval: z.string().optional().describe("once, timed, or always"),
121
+ reason: z.string().optional().describe("Reason for the request")
122
+ }
123
+ }, async ({ command, audience, approval, reason }) => {
124
+ const idp = getIdpUrl();
125
+ const requester = getRequesterIdentity();
126
+ if (!idp || !requester)
127
+ return { content: [{ type: "text", text: "Not authenticated. Run `apes login` first." }] };
128
+ const grantsUrl = await getGrantsEndpoint(idp);
129
+ const cmdParts = command.split(" ");
130
+ const grant = await apiFetch(grantsUrl, {
131
+ method: "POST",
132
+ body: {
133
+ requester,
134
+ target_host: hostname(),
135
+ audience,
136
+ grant_type: approval || "once",
137
+ command: cmdParts,
138
+ reason: reason || command
139
+ }
140
+ });
141
+ return {
142
+ content: [{
143
+ type: "text",
144
+ text: JSON.stringify({
145
+ status: "pending_approval",
146
+ grant_id: grant.id,
147
+ approve_url: `${idp}/grant-approval?grant_id=${grant.id}`,
148
+ message: "Grant needs approval. Approve via URL or `apes grants approve <id>`, then retry with grant_id."
149
+ }, null, 2)
150
+ }]
151
+ };
152
+ });
153
+ server.registerTool("apes.config.get", {
154
+ description: "Get apes configuration value",
155
+ inputSchema: {
156
+ key: z.string().describe("Config key: idp, email")
157
+ }
158
+ }, ({ key }) => {
159
+ if (key === "idp") {
160
+ const idp = getIdpUrl();
161
+ return { content: [{ type: "text", text: idp || "Not configured" }] };
162
+ }
163
+ if (key === "email") {
164
+ const email = getRequesterIdentity();
165
+ return { content: [{ type: "text", text: email || "Not logged in" }] };
166
+ }
167
+ return { content: [{ type: "text", text: `Unknown key: ${key}` }] };
168
+ });
169
+ server.registerTool("apes.explain", {
170
+ description: "Show what permissions a command would need",
171
+ inputSchema: {
172
+ command: z.array(z.string()).describe('Command as array of strings (e.g. ["gh", "repo", "list"])'),
173
+ adapter: z.string().optional().describe("Explicit adapter TOML path")
174
+ }
175
+ }, async ({ command, adapter }) => {
176
+ try {
177
+ const loaded = loadAdapter2(command[0], adapter);
178
+ const resolved = await resolveCommand(loaded, command);
179
+ return {
180
+ content: [{
181
+ type: "text",
182
+ text: JSON.stringify({
183
+ adapter: resolved.adapter.cli.id,
184
+ operation: resolved.detail.operation_id,
185
+ display: resolved.detail.display,
186
+ permission: resolved.permission,
187
+ resource_chain: resolved.detail.resource_chain,
188
+ exact_command: resolved.detail.constraints?.exact_command ?? false,
189
+ adapter_digest: resolved.digest
190
+ }, null, 2)
191
+ }]
192
+ };
193
+ } catch (err) {
194
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
195
+ }
196
+ });
197
+ server.registerTool("apes.adapter.list", {
198
+ description: "List installed CLI adapters"
199
+ }, () => {
200
+ const tools = loadAdapterTools();
201
+ const adapters = [...new Set(tools.map((t) => t.adapterId))];
202
+ return {
203
+ content: [{
204
+ type: "text",
205
+ text: adapters.length > 0 ? `Installed adapters: ${adapters.join(", ")}` : "No adapters installed."
206
+ }]
207
+ };
208
+ });
209
+ server.registerTool("apes.fetch", {
210
+ description: "Make an authenticated HTTP request",
211
+ inputSchema: {
212
+ method: z.string().describe("HTTP method (GET, POST, PUT, DELETE)"),
213
+ url: z.string().describe("URL to fetch"),
214
+ body: z.string().optional().describe("Request body (JSON string)")
215
+ }
216
+ }, async ({ method, url, body }) => {
217
+ const token = getAuthToken();
218
+ if (!token)
219
+ return { content: [{ type: "text", text: "Not authenticated. Run `apes login` first." }], isError: true };
220
+ const response = await fetch(url, {
221
+ method,
222
+ headers: {
223
+ "Authorization": `Bearer ${token}`,
224
+ "Content-Type": "application/json"
225
+ },
226
+ body: body || void 0
227
+ });
228
+ const text = await response.text();
229
+ try {
230
+ return { content: [{ type: "text", text: JSON.stringify(JSON.parse(text), null, 2) }] };
231
+ } catch {
232
+ return { content: [{ type: "text", text }] };
233
+ }
234
+ });
235
+ }
236
+ function registerAdapterTools(server) {
237
+ const adapterTools = loadAdapterTools();
238
+ for (const tool of adapterTools) {
239
+ const schemaShape = {
240
+ grant_id: z.string().optional().describe("Grant ID from a previous pending_approval response")
241
+ };
242
+ if (tool.inputSchema && typeof tool.inputSchema === "object") {
243
+ const props = tool.inputSchema.properties;
244
+ if (props) {
245
+ for (const [key, val] of Object.entries(props)) {
246
+ schemaShape[key] = z.string().optional().describe(val.description || key);
247
+ }
248
+ }
249
+ }
250
+ server.registerTool(tool.name, {
251
+ description: tool.description,
252
+ inputSchema: schemaShape
253
+ }, async (args) => {
254
+ const idp = getIdpUrl();
255
+ if (!idp)
256
+ return { content: [{ type: "text", text: "Not configured. Run `apes login` first." }], isError: true };
257
+ try {
258
+ const loaded = loadAdapter2(tool.adapterId);
259
+ const op = loaded.adapter.operations.find((o) => o.id === tool.operationId);
260
+ if (!op)
261
+ return { content: [{ type: "text", text: `Operation ${tool.operationId} not found` }], isError: true };
262
+ const argv = [loaded.adapter.cli.executable, ...op.command];
263
+ if (op.positionals) {
264
+ for (const pos of op.positionals) {
265
+ if (args[pos])
266
+ argv.push(String(args[pos]));
267
+ }
268
+ }
269
+ if (op.required_options) {
270
+ for (const opt of op.required_options) {
271
+ const name = opt.replace(/^--/, "");
272
+ if (args[name])
273
+ argv.push(opt, String(args[name]));
274
+ }
275
+ }
276
+ const resolved = await resolveCommand(loaded, argv);
277
+ if (args.grant_id) {
278
+ const token = await fetchGrantToken(idp, String(args.grant_id));
279
+ await verifyAndExecute(token, resolved);
280
+ return { content: [{ type: "text", text: "Command executed successfully." }] };
281
+ }
282
+ const grant = await createShapesGrant(resolved, { idp, approval: "once" });
283
+ return {
284
+ content: [{
285
+ type: "text",
286
+ text: JSON.stringify({
287
+ status: "pending_approval",
288
+ grant_id: grant.id,
289
+ approve_url: `${idp}/grant-approval?grant_id=${grant.id}`,
290
+ message: "Grant needs approval. Approve, then call this tool again with grant_id."
291
+ }, null, 2)
292
+ }]
293
+ };
294
+ } catch (err) {
295
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
296
+ }
297
+ });
298
+ }
299
+ }
300
+
301
+ // src/commands/mcp/server.ts
302
+ async function startMcpServer(transport, port) {
303
+ const server = new McpServer({
304
+ name: "apes",
305
+ version: true ? "0.2.0" : "0.1.0"
306
+ });
307
+ registerStaticTools(server);
308
+ registerAdapterTools(server);
309
+ if (transport === "stdio") {
310
+ const stdioTransport = new StdioServerTransport();
311
+ await server.connect(stdioTransport);
312
+ } else if (transport === "sse") {
313
+ const httpServer = createServer(async (req, res) => {
314
+ if (req.url === "/sse" && req.method === "GET") {
315
+ const sseTransport = new SSEServerTransport("/messages", res);
316
+ await server.connect(sseTransport);
317
+ } else if (req.url === "/messages" && req.method === "POST") {
318
+ res.writeHead(200);
319
+ res.end();
320
+ } else {
321
+ res.writeHead(404);
322
+ res.end("Not found");
323
+ }
324
+ });
325
+ httpServer.listen(port, () => {
326
+ consola.info(`MCP SSE server listening on http://localhost:${port}/sse`);
327
+ });
328
+ }
329
+ }
330
+ export {
331
+ startMcpServer
332
+ };
333
+ //# sourceMappingURL=server-4ZIIWOOP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/mcp/server.ts","../src/commands/mcp/tools.ts","../src/commands/mcp/adapter-tools.ts"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'\nimport { createServer } from 'node:http'\nimport consola from 'consola'\nimport { registerAdapterTools, registerStaticTools } from './tools'\n\ndeclare const __VERSION__: string\n\nexport async function startMcpServer(transport: 'stdio' | 'sse', port: number) {\n const server = new McpServer({\n name: 'apes',\n version: typeof __VERSION__ !== 'undefined' ? __VERSION__ : '0.1.0',\n })\n\n // Register static tools\n registerStaticTools(server)\n\n // Register adapter-derived tools\n registerAdapterTools(server)\n\n if (transport === 'stdio') {\n const stdioTransport = new StdioServerTransport()\n await server.connect(stdioTransport)\n }\n else if (transport === 'sse') {\n const httpServer = createServer(async (req, res) => {\n if (req.url === '/sse' && req.method === 'GET') {\n const sseTransport = new SSEServerTransport('/messages', res)\n await server.connect(sseTransport)\n }\n else if (req.url === '/messages' && req.method === 'POST') {\n // SSE message handling is done by the transport\n res.writeHead(200)\n res.end()\n }\n else {\n res.writeHead(404)\n res.end('Not found')\n }\n })\n\n httpServer.listen(port, () => {\n consola.info(`MCP SSE server listening on http://localhost:${port}/sse`)\n })\n }\n}\n","import { hostname } from 'node:os'\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport {\n createShapesGrant,\n fetchGrantToken,\n loadAdapter,\n resolveCommand,\n verifyAndExecute,\n} from '@openape/shapes'\nimport { z } from 'zod'\nimport { getAuthToken, getIdpUrl, getRequesterIdentity } from '../../config'\nimport { apiFetch, getGrantsEndpoint } from '../../http'\nimport { loadAdapterTools } from './adapter-tools'\n\nexport function registerStaticTools(server: McpServer) {\n server.registerTool('apes.grants.list', {\n description: 'List grants',\n inputSchema: {\n status: z.string().optional().describe('Filter by status: pending, approved, denied, revoked, used'),\n limit: z.string().optional().describe('Max results'),\n },\n }, async ({ status, limit }) => {\n const idp = getIdpUrl()\n if (!idp)\n return { content: [{ type: 'text' as const, text: 'Not configured. Run `apes login` first.' }] }\n\n const grantsUrl = await getGrantsEndpoint(idp)\n const params = new URLSearchParams()\n if (status)\n params.set('status', status)\n if (limit)\n params.set('limit', limit)\n const query = params.toString() ? `?${params.toString()}` : ''\n const response = await apiFetch(`${grantsUrl}${query}`)\n return { content: [{ type: 'text' as const, text: JSON.stringify(response, null, 2) }] }\n })\n\n server.registerTool('apes.grants.request', {\n description: 'Request a grant for a command',\n inputSchema: {\n command: z.string().describe('Command to request permission for'),\n audience: z.string().describe('Service identifier (e.g. escapes, proxy, shapes)'),\n approval: z.string().optional().describe('once, timed, or always'),\n reason: z.string().optional().describe('Reason for the request'),\n },\n }, async ({ command, audience, approval, reason }) => {\n const idp = getIdpUrl()\n const requester = getRequesterIdentity()\n if (!idp || !requester)\n return { content: [{ type: 'text' as const, text: 'Not authenticated. Run `apes login` first.' }] }\n\n const grantsUrl = await getGrantsEndpoint(idp)\n const cmdParts = command.split(' ')\n const grant = await apiFetch<{ id: string, status: string }>(grantsUrl, {\n method: 'POST',\n body: {\n requester,\n target_host: hostname(),\n audience,\n grant_type: approval || 'once',\n command: cmdParts,\n reason: reason || command,\n },\n })\n\n return {\n content: [{\n type: 'text' as const,\n text: JSON.stringify({\n status: 'pending_approval',\n grant_id: grant.id,\n approve_url: `${idp}/grant-approval?grant_id=${grant.id}`,\n message: 'Grant needs approval. Approve via URL or `apes grants approve <id>`, then retry with grant_id.',\n }, null, 2),\n }],\n }\n })\n\n server.registerTool('apes.config.get', {\n description: 'Get apes configuration value',\n inputSchema: {\n key: z.string().describe('Config key: idp, email'),\n },\n }, ({ key }) => {\n if (key === 'idp') {\n const idp = getIdpUrl()\n return { content: [{ type: 'text' as const, text: idp || 'Not configured' }] }\n }\n if (key === 'email') {\n const email = getRequesterIdentity()\n return { content: [{ type: 'text' as const, text: email || 'Not logged in' }] }\n }\n return { content: [{ type: 'text' as const, text: `Unknown key: ${key}` }] }\n })\n\n server.registerTool('apes.explain', {\n description: 'Show what permissions a command would need',\n inputSchema: {\n command: z.array(z.string()).describe('Command as array of strings (e.g. [\"gh\", \"repo\", \"list\"])'),\n adapter: z.string().optional().describe('Explicit adapter TOML path'),\n },\n }, async ({ command, adapter }) => {\n try {\n const loaded = loadAdapter(command[0]!, adapter)\n const resolved = await resolveCommand(loaded, command)\n return {\n content: [{\n type: 'text' as const,\n text: JSON.stringify({\n adapter: resolved.adapter.cli.id,\n operation: resolved.detail.operation_id,\n display: resolved.detail.display,\n permission: resolved.permission,\n resource_chain: resolved.detail.resource_chain,\n exact_command: resolved.detail.constraints?.exact_command ?? false,\n adapter_digest: resolved.digest,\n }, null, 2),\n }],\n }\n }\n catch (err) {\n return { content: [{ type: 'text' as const, text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true }\n }\n })\n\n server.registerTool('apes.adapter.list', {\n description: 'List installed CLI adapters',\n }, () => {\n const tools = loadAdapterTools()\n const adapters = [...new Set(tools.map(t => t.adapterId))]\n return {\n content: [{\n type: 'text' as const,\n text: adapters.length > 0\n ? `Installed adapters: ${adapters.join(', ')}`\n : 'No adapters installed.',\n }],\n }\n })\n\n server.registerTool('apes.fetch', {\n description: 'Make an authenticated HTTP request',\n inputSchema: {\n method: z.string().describe('HTTP method (GET, POST, PUT, DELETE)'),\n url: z.string().describe('URL to fetch'),\n body: z.string().optional().describe('Request body (JSON string)'),\n },\n }, async ({ method, url, body }) => {\n const token = getAuthToken()\n if (!token)\n return { content: [{ type: 'text' as const, text: 'Not authenticated. Run `apes login` first.' }], isError: true }\n\n const response = await fetch(url, {\n method,\n headers: {\n 'Authorization': `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: body || undefined,\n })\n const text = await response.text()\n try {\n return { content: [{ type: 'text' as const, text: JSON.stringify(JSON.parse(text), null, 2) }] }\n }\n catch {\n return { content: [{ type: 'text' as const, text }] }\n }\n })\n}\n\nexport function registerAdapterTools(server: McpServer) {\n const adapterTools = loadAdapterTools()\n\n for (const tool of adapterTools) {\n // Build Zod schema from adapter operation\n const schemaShape: Record<string, z.ZodType> = {\n grant_id: z.string().optional().describe('Grant ID from a previous pending_approval response'),\n }\n\n if (tool.inputSchema && typeof tool.inputSchema === 'object') {\n const props = (tool.inputSchema as { properties?: Record<string, { description?: string }> }).properties\n if (props) {\n for (const [key, val] of Object.entries(props)) {\n schemaShape[key] = z.string().optional().describe(val.description || key)\n }\n }\n }\n\n server.registerTool(tool.name, {\n description: tool.description,\n inputSchema: schemaShape,\n }, async (args) => {\n const idp = getIdpUrl()\n if (!idp)\n return { content: [{ type: 'text' as const, text: 'Not configured. Run `apes login` first.' }], isError: true }\n\n try {\n const loaded = loadAdapter(tool.adapterId)\n const op = loaded.adapter.operations.find(o => o.id === tool.operationId)\n if (!op)\n return { content: [{ type: 'text' as const, text: `Operation ${tool.operationId} not found` }], isError: true }\n\n const argv = [loaded.adapter.cli.executable, ...op.command]\n if (op.positionals) {\n for (const pos of op.positionals) {\n if (args[pos])\n argv.push(String(args[pos]))\n }\n }\n if (op.required_options) {\n for (const opt of op.required_options) {\n const name = opt.replace(/^--/, '')\n if (args[name])\n argv.push(opt, String(args[name]))\n }\n }\n\n const resolved = await resolveCommand(loaded, argv)\n\n if (args.grant_id) {\n const token = await fetchGrantToken(idp, String(args.grant_id))\n await verifyAndExecute(token, resolved)\n return { content: [{ type: 'text' as const, text: 'Command executed successfully.' }] }\n }\n\n const grant = await createShapesGrant(resolved, { idp, approval: 'once' })\n return {\n content: [{\n type: 'text' as const,\n text: JSON.stringify({\n status: 'pending_approval',\n grant_id: grant.id,\n approve_url: `${idp}/grant-approval?grant_id=${grant.id}`,\n message: 'Grant needs approval. Approve, then call this tool again with grant_id.',\n }, null, 2),\n }],\n }\n }\n catch (err) {\n return { content: [{ type: 'text' as const, text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true }\n }\n })\n }\n}\n","import { readdirSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { loadAdapter } from '@openape/shapes'\nimport type { ShapesOperation } from '@openape/shapes'\n\nexport interface AdapterToolDef {\n name: string\n description: string\n inputSchema: Record<string, unknown>\n adapterId: string\n operationId: string\n}\n\nfunction operationToInputSchema(op: ShapesOperation): Record<string, unknown> {\n const properties: Record<string, unknown> = {}\n const required: string[] = []\n\n if (op.positionals) {\n for (const pos of op.positionals) {\n properties[pos] = { type: 'string', description: `Positional argument: ${pos}` }\n required.push(pos)\n }\n }\n\n if (op.required_options) {\n for (const opt of op.required_options) {\n const name = opt.replace(/^--/, '')\n properties[name] = { type: 'string', description: `Required option: ${opt}` }\n required.push(name)\n }\n }\n\n return {\n type: 'object',\n properties,\n required: required.length > 0 ? required : undefined,\n }\n}\n\nfunction scanAdapterDir(dir: string): string[] {\n try {\n return readdirSync(dir)\n .filter(f => f.endsWith('.toml'))\n .map(f => f.replace('.toml', ''))\n }\n catch {\n return []\n }\n}\n\nexport function loadAdapterTools(): AdapterToolDef[] {\n const tools: AdapterToolDef[] = []\n const seen = new Set<string>()\n\n const adapterDirs = [\n join(process.cwd(), '.openape', 'shapes', 'adapters'),\n join(homedir(), '.openape', 'shapes', 'adapters'),\n '/etc/openape/shapes/adapters',\n ]\n\n for (const dir of adapterDirs) {\n for (const id of scanAdapterDir(dir)) {\n if (seen.has(id))\n continue\n seen.add(id)\n\n try {\n const loaded = loadAdapter(id)\n for (const op of loaded.adapter.operations) {\n tools.push({\n name: `apes.run.${id}.${op.id}`,\n description: op.display || `${id}: ${op.id}`,\n inputSchema: operationToInputSchema(op),\n adapterId: id,\n operationId: op.id,\n })\n }\n }\n catch {\n // Skip adapters that fail to load\n }\n }\n }\n\n return tools\n}\n"],"mappings":"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","names":["loadAdapter","loadAdapter"]}
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/ssh-key.ts
4
+ import { Buffer } from "buffer";
5
+ import { createPrivateKey } from "crypto";
6
+ var OPENSSH_MAGIC = "openssh-key-v1\0";
7
+ function loadEd25519PrivateKey(pem) {
8
+ if (pem.includes("BEGIN OPENSSH PRIVATE KEY")) {
9
+ return parseOpenSSHEd25519(pem);
10
+ }
11
+ return createPrivateKey(pem);
12
+ }
13
+ function parseOpenSSHEd25519(pem) {
14
+ const b64 = pem.replace(/-----BEGIN OPENSSH PRIVATE KEY-----/, "").replace(/-----END OPENSSH PRIVATE KEY-----/, "").replace(/\s/g, "");
15
+ const buf = Buffer.from(b64, "base64");
16
+ let offset = 0;
17
+ const magic = buf.subarray(0, OPENSSH_MAGIC.length).toString("ascii");
18
+ if (magic !== OPENSSH_MAGIC) {
19
+ throw new Error("Not an OpenSSH private key");
20
+ }
21
+ offset += OPENSSH_MAGIC.length;
22
+ const cipherLen = buf.readUInt32BE(offset);
23
+ offset += 4;
24
+ const cipher = buf.subarray(offset, offset + cipherLen).toString();
25
+ offset += cipherLen;
26
+ if (cipher !== "none") {
27
+ throw new Error(`Encrypted keys not supported (cipher: ${cipher}). Decrypt first with: ssh-keygen -p -f <key>`);
28
+ }
29
+ const kdfLen = buf.readUInt32BE(offset);
30
+ offset += 4;
31
+ offset += kdfLen;
32
+ const kdfOptsLen = buf.readUInt32BE(offset);
33
+ offset += 4;
34
+ offset += kdfOptsLen;
35
+ const numKeys = buf.readUInt32BE(offset);
36
+ offset += 4;
37
+ if (numKeys !== 1) {
38
+ throw new Error(`Expected 1 key, got ${numKeys}`);
39
+ }
40
+ const pubSectionLen = buf.readUInt32BE(offset);
41
+ offset += 4;
42
+ offset += pubSectionLen;
43
+ const privSectionLen = buf.readUInt32BE(offset);
44
+ offset += 4;
45
+ const privSection = buf.subarray(offset, offset + privSectionLen);
46
+ let pOffset = 0;
47
+ const check1 = privSection.readUInt32BE(pOffset);
48
+ pOffset += 4;
49
+ const check2 = privSection.readUInt32BE(pOffset);
50
+ pOffset += 4;
51
+ if (check1 !== check2) {
52
+ throw new Error("Check integers mismatch \u2014 key may be corrupted or encrypted");
53
+ }
54
+ const keyTypeLen = privSection.readUInt32BE(pOffset);
55
+ pOffset += 4;
56
+ const keyType = privSection.subarray(pOffset, pOffset + keyTypeLen).toString();
57
+ pOffset += keyTypeLen;
58
+ if (keyType !== "ssh-ed25519") {
59
+ throw new Error(`Expected ssh-ed25519, got ${keyType}`);
60
+ }
61
+ const pubKeyLen = privSection.readUInt32BE(pOffset);
62
+ pOffset += 4;
63
+ const pubKey = privSection.subarray(pOffset, pOffset + pubKeyLen);
64
+ pOffset += pubKeyLen;
65
+ const privKeyLen = privSection.readUInt32BE(pOffset);
66
+ pOffset += 4;
67
+ const privKeyData = privSection.subarray(pOffset, pOffset + privKeyLen);
68
+ const seed = privKeyData.subarray(0, 32);
69
+ const d = seed.toString("base64url");
70
+ const x = pubKey.toString("base64url");
71
+ return createPrivateKey({
72
+ key: { kty: "OKP", crv: "Ed25519", d, x },
73
+ format: "jwk"
74
+ });
75
+ }
76
+ export {
77
+ loadEd25519PrivateKey
78
+ };
79
+ //# sourceMappingURL=ssh-key-ABJCJDH7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ssh-key.ts"],"sourcesContent":["import { Buffer } from 'node:buffer'\nimport type { KeyObject } from 'node:crypto'\nimport { createPrivateKey } from 'node:crypto'\n\nconst OPENSSH_MAGIC = 'openssh-key-v1\\0'\n\n/**\n * Parse an OpenSSH Ed25519 private key file and return a Node.js KeyObject.\n * Supports both OpenSSH format (-----BEGIN OPENSSH PRIVATE KEY-----)\n * and PKCS8 PEM format (-----BEGIN PRIVATE KEY-----).\n */\nexport function loadEd25519PrivateKey(pem: string): KeyObject {\n if (pem.includes('BEGIN OPENSSH PRIVATE KEY')) {\n return parseOpenSSHEd25519(pem)\n }\n // PKCS8 PEM — Node.js handles this natively\n return createPrivateKey(pem)\n}\n\nfunction parseOpenSSHEd25519(pem: string): KeyObject {\n const b64 = pem\n .replace(/-----BEGIN OPENSSH PRIVATE KEY-----/, '')\n .replace(/-----END OPENSSH PRIVATE KEY-----/, '')\n .replace(/\\s/g, '')\n\n const buf = Buffer.from(b64, 'base64')\n let offset = 0\n\n // Verify magic\n const magic = buf.subarray(0, OPENSSH_MAGIC.length).toString('ascii')\n if (magic !== OPENSSH_MAGIC) {\n throw new Error('Not an OpenSSH private key')\n }\n offset += OPENSSH_MAGIC.length\n\n // ciphername\n const cipherLen = buf.readUInt32BE(offset); offset += 4\n const cipher = buf.subarray(offset, offset + cipherLen).toString(); offset += cipherLen\n if (cipher !== 'none') {\n throw new Error(`Encrypted keys not supported (cipher: ${cipher}). Decrypt first with: ssh-keygen -p -f <key>`)\n }\n\n // kdfname\n const kdfLen = buf.readUInt32BE(offset); offset += 4\n offset += kdfLen // skip kdf\n\n // kdfoptions\n const kdfOptsLen = buf.readUInt32BE(offset); offset += 4\n offset += kdfOptsLen // skip\n\n // number of keys\n const numKeys = buf.readUInt32BE(offset); offset += 4\n if (numKeys !== 1) {\n throw new Error(`Expected 1 key, got ${numKeys}`)\n }\n\n // public key section (skip)\n const pubSectionLen = buf.readUInt32BE(offset); offset += 4\n offset += pubSectionLen\n\n // private key section\n const privSectionLen = buf.readUInt32BE(offset); offset += 4\n const privSection = buf.subarray(offset, offset + privSectionLen)\n let pOffset = 0\n\n // check integers (must match)\n const check1 = privSection.readUInt32BE(pOffset); pOffset += 4\n const check2 = privSection.readUInt32BE(pOffset); pOffset += 4\n if (check1 !== check2) {\n throw new Error('Check integers mismatch — key may be corrupted or encrypted')\n }\n\n // keytype string\n const keyTypeLen = privSection.readUInt32BE(pOffset); pOffset += 4\n const keyType = privSection.subarray(pOffset, pOffset + keyTypeLen).toString(); pOffset += keyTypeLen\n if (keyType !== 'ssh-ed25519') {\n throw new Error(`Expected ssh-ed25519, got ${keyType}`)\n }\n\n // public key (32 bytes, prefixed with length)\n const pubKeyLen = privSection.readUInt32BE(pOffset); pOffset += 4\n const pubKey = privSection.subarray(pOffset, pOffset + pubKeyLen); pOffset += pubKeyLen\n\n // private key (64 bytes = 32-byte seed + 32-byte pubkey, prefixed with length)\n const privKeyLen = privSection.readUInt32BE(pOffset); pOffset += 4\n const privKeyData = privSection.subarray(pOffset, pOffset + privKeyLen)\n\n const seed = privKeyData.subarray(0, 32) // Ed25519 seed\n\n // Import as JWK\n const d = seed.toString('base64url')\n const x = pubKey.toString('base64url')\n\n return createPrivateKey({\n key: { kty: 'OKP', crv: 'Ed25519', d, x },\n format: 'jwk',\n })\n}\n"],"mappings":";;;AAAA,SAAS,cAAc;AAEvB,SAAS,wBAAwB;AAEjC,IAAM,gBAAgB;AAOf,SAAS,sBAAsB,KAAwB;AAC5D,MAAI,IAAI,SAAS,2BAA2B,GAAG;AAC7C,WAAO,oBAAoB,GAAG;AAAA,EAChC;AAEA,SAAO,iBAAiB,GAAG;AAC7B;AAEA,SAAS,oBAAoB,KAAwB;AACnD,QAAM,MAAM,IACT,QAAQ,uCAAuC,EAAE,EACjD,QAAQ,qCAAqC,EAAE,EAC/C,QAAQ,OAAO,EAAE;AAEpB,QAAM,MAAM,OAAO,KAAK,KAAK,QAAQ;AACrC,MAAI,SAAS;AAGb,QAAM,QAAQ,IAAI,SAAS,GAAG,cAAc,MAAM,EAAE,SAAS,OAAO;AACpE,MAAI,UAAU,eAAe;AAC3B,UAAM,IAAI,MAAM,4BAA4B;AAAA,EAC9C;AACA,YAAU,cAAc;AAGxB,QAAM,YAAY,IAAI,aAAa,MAAM;AAAG,YAAU;AACtD,QAAM,SAAS,IAAI,SAAS,QAAQ,SAAS,SAAS,EAAE,SAAS;AAAG,YAAU;AAC9E,MAAI,WAAW,QAAQ;AACrB,UAAM,IAAI,MAAM,yCAAyC,MAAM,+CAA+C;AAAA,EAChH;AAGA,QAAM,SAAS,IAAI,aAAa,MAAM;AAAG,YAAU;AACnD,YAAU;AAGV,QAAM,aAAa,IAAI,aAAa,MAAM;AAAG,YAAU;AACvD,YAAU;AAGV,QAAM,UAAU,IAAI,aAAa,MAAM;AAAG,YAAU;AACpD,MAAI,YAAY,GAAG;AACjB,UAAM,IAAI,MAAM,uBAAuB,OAAO,EAAE;AAAA,EAClD;AAGA,QAAM,gBAAgB,IAAI,aAAa,MAAM;AAAG,YAAU;AAC1D,YAAU;AAGV,QAAM,iBAAiB,IAAI,aAAa,MAAM;AAAG,YAAU;AAC3D,QAAM,cAAc,IAAI,SAAS,QAAQ,SAAS,cAAc;AAChE,MAAI,UAAU;AAGd,QAAM,SAAS,YAAY,aAAa,OAAO;AAAG,aAAW;AAC7D,QAAM,SAAS,YAAY,aAAa,OAAO;AAAG,aAAW;AAC7D,MAAI,WAAW,QAAQ;AACrB,UAAM,IAAI,MAAM,kEAA6D;AAAA,EAC/E;AAGA,QAAM,aAAa,YAAY,aAAa,OAAO;AAAG,aAAW;AACjE,QAAM,UAAU,YAAY,SAAS,SAAS,UAAU,UAAU,EAAE,SAAS;AAAG,aAAW;AAC3F,MAAI,YAAY,eAAe;AAC7B,UAAM,IAAI,MAAM,6BAA6B,OAAO,EAAE;AAAA,EACxD;AAGA,QAAM,YAAY,YAAY,aAAa,OAAO;AAAG,aAAW;AAChE,QAAM,SAAS,YAAY,SAAS,SAAS,UAAU,SAAS;AAAG,aAAW;AAG9E,QAAM,aAAa,YAAY,aAAa,OAAO;AAAG,aAAW;AACjE,QAAM,cAAc,YAAY,SAAS,SAAS,UAAU,UAAU;AAEtE,QAAM,OAAO,YAAY,SAAS,GAAG,EAAE;AAGvC,QAAM,IAAI,KAAK,SAAS,WAAW;AACnC,QAAM,IAAI,OAAO,SAAS,WAAW;AAErC,SAAO,iBAAiB;AAAA,IACtB,KAAK,EAAE,KAAK,OAAO,KAAK,WAAW,GAAG,EAAE;AAAA,IACxC,QAAQ;AAAA,EACV,CAAC;AACH;","names":[]}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@openape/apes",
3
+ "version": "0.2.0",
4
+ "description": "Unified CLI + MCP Server for OpenApe",
5
+ "type": "module",
6
+ "bin": {
7
+ "apes": "./dist/cli.js"
8
+ },
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "dependencies": {
19
+ "@modelcontextprotocol/sdk": "^1.27.1",
20
+ "citty": "^0.1.6",
21
+ "consola": "^3.4.2",
22
+ "zod": "^4.3.6",
23
+ "@openape/core": "0.9.0",
24
+ "@openape/shapes": "0.4.0",
25
+ "@openape/grants": "0.5.2"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^25.3.5",
29
+ "tsup": "^8.5.1",
30
+ "typescript": "^5.8.2",
31
+ "vitest": "^3.2.4"
32
+ },
33
+ "license": "MIT",
34
+ "author": "Patrick Hofmann <phofmann@delta-mind.at>",
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "https://github.com/openape-ai/openape.git",
41
+ "directory": "packages/apes"
42
+ },
43
+ "scripts": {
44
+ "build": "tsup",
45
+ "lint": "eslint .",
46
+ "typecheck": "tsc --noEmit",
47
+ "test": "vitest run"
48
+ }
49
+ }