@map-audio/pam-mcp-server 1.0.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 ADDED
@@ -0,0 +1,60 @@
1
+ # @map-audio/pam-mcp-server
2
+
3
+ MCP server for the [PAM](https://map.audio) audio plugin. Lets AI agents (Claude Desktop, Claude Code, Cursor, etc.) control a running PAM instance — load samples, set parameters, trigger MIDI, edit patterns, manage presets, run the randomizer, and more.
4
+
5
+ ## Requirements
6
+
7
+ - macOS or Windows with **Node.js 21+** (`node --version`)
8
+ - **PAM** installed and running (standalone or in a DAW)
9
+ - **MCP Bridge enabled** inside PAM: `Settings → Experimental Mode → MCP Bridge` (default port `9847`)
10
+
11
+ When PAM starts with the bridge enabled it writes `~/.pam-mcp-bridge.json` — the server discovers PAM through that file, so nothing else needs configuring.
12
+
13
+ ## Install
14
+
15
+ You do **not** need to clone anything. Add the server to your MCP client's config file and it will be fetched on first launch via `npx`.
16
+
17
+ ### Claude Desktop
18
+
19
+ Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
20
+
21
+ ```json
22
+ {
23
+ "mcpServers": {
24
+ "pam": {
25
+ "command": "npx",
26
+ "args": ["-y", "@map-audio/pam-mcp-server"]
27
+ }
28
+ }
29
+ }
30
+ ```
31
+
32
+ Fully quit Claude Desktop (`Cmd+Q` / right-click tray icon → Quit) and reopen. PAM's tools will appear in the MCP menu.
33
+
34
+ ### Claude Code
35
+
36
+ ```bash
37
+ claude mcp add pam -- npx -y @map-audio/pam-mcp-server
38
+ ```
39
+
40
+ or add the same `mcpServers` block to `.mcp.json` at your project root.
41
+
42
+ ### Cursor / other MCP clients
43
+
44
+ Use the same `mcpServers` block in the client's MCP config. Any stdio-compatible MCP client works.
45
+
46
+ ## Troubleshooting
47
+
48
+ - **"Not connected to PAM plugin"** — PAM isn't running, or MCP Bridge is off. Open PAM → Settings → Experimental Mode → enable MCP Bridge, then restart PAM.
49
+ - **`npx: command not found`** (macOS GUI apps) — GUI apps don't inherit shell PATH. Install Node from [nodejs.org](https://nodejs.org) (not via `nvm`/`asdf`), or give the absolute path: `"command": "/usr/local/bin/npx"` / `"/opt/homebrew/bin/npx"`.
50
+ - **Offline tools fail** (`list_presets`, `get_parameter_info`) — the manifest bundled with the package is used automatically. To target a different PAM install, set `PAM_MANIFEST_PATH` in the server's `env` block.
51
+
52
+ ## What's in the box
53
+
54
+ Live tools (require a running PAM): `set_parameters`, `trigger_midi`, `update_pattern`, `load_sample`, `load_preset`, `save_preset`, `add_modulation`, `start_transport`, `stop_transport`, `full_stop_transport`, `set_bpm`, `randomize_samples`, `randomize_patterns`, `load_variation`, and more.
55
+
56
+ Offline tools (manifest-only): `get_parameter_info`, `list_presets`, `list_samples`, `get_manifest_summary`.
57
+
58
+ ## License
59
+
60
+ MIT
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * PAM Agent — bridges any OpenAI-compatible local LLM to the PAM MCP server.
4
+ *
5
+ * Usage:
6
+ * npx tsx packages/mcp-server/src/agent.ts # defaults to Ollama
7
+ * npx tsx packages/mcp-server/src/agent.ts --url http://localhost:1234/v1 # LM Studio
8
+ * npx tsx packages/mcp-server/src/agent.ts --model qwen2.5:14b # specific model
9
+ * PAM_LLM_URL=http://localhost:11434/v1 npx tsx packages/mcp-server/src/agent.ts
10
+ */
11
+ export {};
package/dist/agent.js ADDED
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * PAM Agent — bridges any OpenAI-compatible local LLM to the PAM MCP server.
4
+ *
5
+ * Usage:
6
+ * npx tsx packages/mcp-server/src/agent.ts # defaults to Ollama
7
+ * npx tsx packages/mcp-server/src/agent.ts --url http://localhost:1234/v1 # LM Studio
8
+ * npx tsx packages/mcp-server/src/agent.ts --model qwen2.5:14b # specific model
9
+ * PAM_LLM_URL=http://localhost:11434/v1 npx tsx packages/mcp-server/src/agent.ts
10
+ */
11
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
12
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
13
+ import { createInterface } from "node:readline";
14
+ import { resolve, dirname } from "node:path";
15
+ import { fileURLToPath } from "node:url";
16
+ // --- Config ---
17
+ const __dirname = dirname(fileURLToPath(import.meta.url));
18
+ const SERVER_SCRIPT = resolve(__dirname, "server.ts");
19
+ function parseArgs() {
20
+ const args = process.argv.slice(2);
21
+ let url = process.env.PAM_LLM_URL || "http://localhost:1234/v1";
22
+ let model = process.env.PAM_LLM_MODEL || "google/gemma-4-26b-a4b";
23
+ for (let i = 0; i < args.length; i++) {
24
+ if (args[i] === "--url" && args[i + 1])
25
+ url = args[i + 1];
26
+ if (args[i] === "--model" && args[i + 1])
27
+ model = args[i + 1];
28
+ }
29
+ return { url, model };
30
+ }
31
+ const config = parseArgs();
32
+ async function resolveModel() {
33
+ if (config.model)
34
+ return;
35
+ try {
36
+ const resp = await fetch(`${config.url}/models`);
37
+ const data = (await resp.json());
38
+ const models = (data.data || []).filter((m) => !m.id.includes("embed"));
39
+ if (models.length > 0) {
40
+ config.model = models[0].id;
41
+ }
42
+ }
43
+ catch {
44
+ // fall through
45
+ }
46
+ if (!config.model) {
47
+ console.error("No model specified and auto-detection failed. Use --model <name>");
48
+ process.exit(1);
49
+ }
50
+ }
51
+ // --- MCP Client ---
52
+ async function createMcpClient() {
53
+ const transport = new StdioClientTransport({
54
+ command: "npx",
55
+ args: ["tsx", SERVER_SCRIPT],
56
+ stderr: "inherit",
57
+ });
58
+ const client = new Client({ name: "pam-agent", version: "1.0.0" });
59
+ await client.connect(transport);
60
+ return client;
61
+ }
62
+ function mcpToolsToOpenAI(tools) {
63
+ return tools.map((t) => ({
64
+ type: "function",
65
+ function: {
66
+ name: t.name,
67
+ description: t.description || "",
68
+ parameters: t.inputSchema,
69
+ },
70
+ }));
71
+ }
72
+ async function chatCompletion(messages, tools) {
73
+ const body = {
74
+ model: config.model,
75
+ messages,
76
+ temperature: 0.7,
77
+ };
78
+ if (tools.length > 0) {
79
+ body.tools = tools;
80
+ body.tool_choice = "auto";
81
+ }
82
+ const resp = await fetch(`${config.url}/chat/completions`, {
83
+ method: "POST",
84
+ headers: { "Content-Type": "application/json" },
85
+ body: JSON.stringify(body),
86
+ });
87
+ if (!resp.ok) {
88
+ const text = await resp.text();
89
+ throw new Error(`LLM API error ${resp.status}: ${text}`);
90
+ }
91
+ const data = (await resp.json());
92
+ return data.choices[0].message;
93
+ }
94
+ // --- System Prompt ---
95
+ const SYSTEM_PROMPT = `You are PAM Assistant — an AI that controls the PAM audio plugin (sampler/synthesizer).
96
+
97
+ You have access to tools that let you:
98
+ - Query parameter definitions (ranges, defaults, effects)
99
+ - Read and list presets
100
+ - Set parameters on the running plugin
101
+ - Load samples and presets
102
+ - Trigger MIDI notes
103
+ - Add modulation (LFO, envelopes, vary)
104
+ - Update sequencer patterns
105
+ - Randomize sounds
106
+
107
+ When the user asks you to make a sound or adjust the plugin:
108
+ 1. Use get_parameter_info or get_manifest_summary to understand available parameters
109
+ 2. Use set_parameters to change values (always check ranges first)
110
+ 3. Use trigger_midi to preview the result
111
+
112
+ Be concise. Show what you changed. If the plugin isn't connected, offline tools (parameter info, presets) still work.`;
113
+ // --- Agent Loop ---
114
+ async function run() {
115
+ await resolveModel();
116
+ console.log(`\x1b[36mPAM Agent\x1b[0m — connecting to MCP server...`);
117
+ const client = await createMcpClient();
118
+ const { tools: mcpTools } = await client.listTools();
119
+ const openaiTools = mcpToolsToOpenAI(mcpTools);
120
+ console.log(`\x1b[32mReady\x1b[0m — ${mcpTools.length} tools loaded, using ${config.model} at ${config.url}`);
121
+ console.log(`Type your message (Ctrl+C to quit)\n`);
122
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
123
+ const messages = [{ role: "system", content: SYSTEM_PROMPT }];
124
+ const prompt = () => new Promise((resolve) => rl.question("\x1b[33myou>\x1b[0m ", resolve));
125
+ while (true) {
126
+ const userInput = await prompt();
127
+ if (!userInput.trim())
128
+ continue;
129
+ messages.push({ role: "user", content: userInput });
130
+ // Agent loop — keep going until we get a text response (no tool calls)
131
+ let iterations = 0;
132
+ const MAX_ITERATIONS = 20;
133
+ while (iterations++ < MAX_ITERATIONS) {
134
+ let response;
135
+ try {
136
+ response = await chatCompletion(messages, openaiTools);
137
+ }
138
+ catch (e) {
139
+ console.error(`\x1b[31mLLM error:\x1b[0m ${e.message}`);
140
+ break;
141
+ }
142
+ messages.push(response);
143
+ // No tool calls — print the text response and break
144
+ if (!response.tool_calls || response.tool_calls.length === 0) {
145
+ if (response.content) {
146
+ console.log(`\n\x1b[36mpam>\x1b[0m ${response.content}\n`);
147
+ }
148
+ break;
149
+ }
150
+ // Execute tool calls
151
+ for (const tc of response.tool_calls) {
152
+ const { name, arguments: argsStr } = tc.function;
153
+ let args;
154
+ try {
155
+ args = JSON.parse(argsStr);
156
+ }
157
+ catch {
158
+ args = {};
159
+ }
160
+ console.log(` \x1b[90m⚡ ${name}(${JSON.stringify(args)})\x1b[0m`);
161
+ try {
162
+ const result = await client.callTool({ name, arguments: args });
163
+ const contentArr = Array.isArray(result.content) ? result.content : [];
164
+ const text = contentArr
165
+ .filter((c) => c.type === "text")
166
+ .map((c) => c.text || "")
167
+ .join("\n") || "";
168
+ messages.push({
169
+ role: "tool",
170
+ tool_call_id: tc.id,
171
+ content: text,
172
+ });
173
+ // Show tool results for visibility
174
+ if (text.length < 500) {
175
+ console.log(` \x1b[90m→ ${text}\x1b[0m`);
176
+ }
177
+ else {
178
+ console.log(` \x1b[90m→ (${text.length} chars)\x1b[0m`);
179
+ }
180
+ }
181
+ catch (e) {
182
+ const errMsg = `Tool error: ${e.message}`;
183
+ messages.push({
184
+ role: "tool",
185
+ tool_call_id: tc.id,
186
+ content: errMsg,
187
+ });
188
+ console.log(` \x1b[31m→ ${errMsg}\x1b[0m`);
189
+ }
190
+ }
191
+ }
192
+ if (iterations >= MAX_ITERATIONS) {
193
+ console.log(`\x1b[31mMax tool iterations reached.\x1b[0m\n`);
194
+ }
195
+ }
196
+ }
197
+ run().catch((e) => {
198
+ console.error("Fatal:", e.message);
199
+ process.exit(1);
200
+ });
201
+ //# sourceMappingURL=agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ interface BridgeInfo {
2
+ port: number;
3
+ instanceId: string;
4
+ product: string;
5
+ pid: number;
6
+ }
7
+ export declare class PluginBridge {
8
+ private socket;
9
+ private buffer;
10
+ private nextId;
11
+ private pending;
12
+ private connected;
13
+ discover(): Promise<BridgeInfo[]>;
14
+ connect(port?: number): Promise<boolean>;
15
+ disconnect(): void;
16
+ isConnected(): boolean;
17
+ private static readonly SLOW_ACTIONS;
18
+ send(action: string, ...args: unknown[]): Promise<unknown>;
19
+ private processBuffer;
20
+ }
21
+ export {};
package/dist/bridge.js ADDED
@@ -0,0 +1,147 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { resolve } from "node:path";
3
+ import { createConnection } from "node:net";
4
+ import { homedir } from "node:os";
5
+ const DISCOVERY_FILE = resolve(homedir(), ".pam-mcp-bridge.json");
6
+ export class PluginBridge {
7
+ socket = null;
8
+ buffer = "";
9
+ nextId = 1;
10
+ pending = new Map();
11
+ connected = false;
12
+ async discover() {
13
+ try {
14
+ const raw = await readFile(DISCOVERY_FILE, "utf-8");
15
+ const data = JSON.parse(raw);
16
+ const entries = Array.isArray(data) ? data : [data];
17
+ // Filter stale entries whose pid is no longer alive so we don't spend
18
+ // 2s on a TCP timeout per dead instance. process.kill(pid, 0) throws
19
+ // ESRCH if the process doesn't exist, EPERM if it exists but we can't
20
+ // signal it (still alive).
21
+ return entries.filter((e) => {
22
+ if (!e || typeof e.pid !== "number" || e.pid <= 0)
23
+ return false;
24
+ try {
25
+ process.kill(e.pid, 0);
26
+ return true;
27
+ }
28
+ catch (err) {
29
+ return err.code === "EPERM";
30
+ }
31
+ });
32
+ }
33
+ catch {
34
+ return [];
35
+ }
36
+ }
37
+ async connect(port) {
38
+ if (this.connected)
39
+ return true;
40
+ if (!port) {
41
+ const instances = await this.discover();
42
+ if (instances.length === 0)
43
+ return false;
44
+ port = instances[0].port;
45
+ }
46
+ return new Promise((resolve) => {
47
+ const timer = setTimeout(() => {
48
+ if (!this.connected) {
49
+ this.socket?.destroy();
50
+ resolve(false);
51
+ }
52
+ }, 2000);
53
+ this.socket = createConnection({ host: "127.0.0.1", port }, () => {
54
+ clearTimeout(timer);
55
+ this.connected = true;
56
+ resolve(true);
57
+ });
58
+ this.socket.setEncoding("utf-8");
59
+ this.socket.on("data", (chunk) => {
60
+ this.buffer += chunk;
61
+ this.processBuffer();
62
+ });
63
+ this.socket.on("error", () => {
64
+ clearTimeout(timer);
65
+ this.connected = false;
66
+ resolve(false);
67
+ });
68
+ this.socket.on("close", () => {
69
+ this.connected = false;
70
+ // Reject all pending requests
71
+ for (const [, req] of this.pending) {
72
+ req.reject(new Error("Connection closed"));
73
+ }
74
+ this.pending.clear();
75
+ });
76
+ });
77
+ }
78
+ disconnect() {
79
+ this.socket?.destroy();
80
+ this.socket = null;
81
+ this.connected = false;
82
+ }
83
+ isConnected() {
84
+ return this.connected;
85
+ }
86
+ // Actions that block the message thread for disk I/O + graph rebuild
87
+ static SLOW_ACTIONS = new Set([
88
+ "loadPreset",
89
+ "savePreset",
90
+ "randomize",
91
+ "randomizeSamples",
92
+ "importPreset",
93
+ "exportPresetWithSamples",
94
+ "resetToInitialState",
95
+ "loadSampleToCell",
96
+ "loadVariation",
97
+ "listDirectory",
98
+ "importVisualFolder",
99
+ ]);
100
+ async send(action, ...args) {
101
+ if (!this.connected || !this.socket) {
102
+ throw new Error("Not connected to PAM plugin. Start PAM and ensure the MCP bridge is enabled.");
103
+ }
104
+ const id = this.nextId++;
105
+ const msg = { id, action, args };
106
+ const timeoutMs = PluginBridge.SLOW_ACTIONS.has(action) ? 30000 : 10000;
107
+ return new Promise((resolve, reject) => {
108
+ const timer = setTimeout(() => {
109
+ if (this.pending.has(id)) {
110
+ this.pending.delete(id);
111
+ reject(new Error(`Timeout waiting for response to ${action}`));
112
+ }
113
+ }, timeoutMs);
114
+ this.pending.set(id, {
115
+ resolve: (v) => { clearTimeout(timer); resolve(v); },
116
+ reject: (e) => { clearTimeout(timer); reject(e); },
117
+ });
118
+ this.socket.write(JSON.stringify(msg) + "\n");
119
+ });
120
+ }
121
+ processBuffer() {
122
+ const lines = this.buffer.split("\n");
123
+ // Keep the last incomplete line in the buffer
124
+ this.buffer = lines.pop() || "";
125
+ for (const line of lines) {
126
+ if (!line.trim())
127
+ continue;
128
+ try {
129
+ const resp = JSON.parse(line);
130
+ const pending = this.pending.get(resp.id);
131
+ if (pending) {
132
+ this.pending.delete(resp.id);
133
+ if (resp.error) {
134
+ pending.reject(new Error(resp.error));
135
+ }
136
+ else {
137
+ pending.resolve(resp.result);
138
+ }
139
+ }
140
+ }
141
+ catch {
142
+ // Ignore malformed responses
143
+ }
144
+ }
145
+ }
146
+ }
147
+ //# sourceMappingURL=bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ export interface ManifestParameter {
2
+ paramId: string;
3
+ name: string;
4
+ displayName: string;
5
+ min: number;
6
+ max: number;
7
+ defaultValue: number;
8
+ step: number;
9
+ skew: number;
10
+ suffix?: string;
11
+ bipolar?: boolean;
12
+ strRepr?: string[];
13
+ tooltip?: string;
14
+ cell?: string;
15
+ effect?: string;
16
+ isPluginParameter?: boolean;
17
+ isModulatable?: boolean;
18
+ version?: number;
19
+ }
20
+ export interface Manifest {
21
+ name: string;
22
+ company: string;
23
+ bundleId: string;
24
+ pluginCode: string;
25
+ parameters: ManifestParameter[];
26
+ window: {
27
+ width: number;
28
+ height: number;
29
+ };
30
+ }
31
+ export declare function loadManifest(): Promise<Manifest>;
32
+ export declare function getParameterIndex(): Promise<Map<string, ManifestParameter>>;
33
+ export declare function getParametersByCell(cellId: string): Promise<ManifestParameter[]>;
34
+ export declare function getGlobalParameters(): Promise<ManifestParameter[]>;
35
+ export declare function findPresetDirs(): Promise<string[]>;
36
+ export declare function resolvePresetPath(presetPath: string): Promise<string>;
37
+ export declare function walkPresets(dir: string, prefix?: string): Promise<{
38
+ path: string;
39
+ name: string;
40
+ relativePath: string;
41
+ }[]>;
@@ -0,0 +1,135 @@
1
+ import { readFile, readdir, stat } from "node:fs/promises";
2
+ import { resolve, dirname, join, basename, extname, sep } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { homedir } from "node:os";
5
+ let cachedManifest = null;
6
+ let paramIndex = null;
7
+ function getProjectRoot() {
8
+ // Allow override via env var for standalone / outside-repo usage
9
+ if (process.env.PAM_PROJECT_ROOT) {
10
+ return resolve(process.env.PAM_PROJECT_ROOT);
11
+ }
12
+ const __dirname = dirname(fileURLToPath(import.meta.url));
13
+ // packages/mcp-server/src (or dist) -> project root
14
+ return resolve(__dirname, "..", "..", "..");
15
+ }
16
+ function findManifestPath() {
17
+ const root = getProjectRoot();
18
+ // Primary: manifest.json at project root
19
+ const rootManifest = resolve(root, "manifest.json");
20
+ // Fallback: bundled manifest next to compiled server (dist/manifest.json)
21
+ const __dirname = dirname(fileURLToPath(import.meta.url));
22
+ const bundledManifest = resolve(__dirname, "manifest.json");
23
+ // Check env override
24
+ if (process.env.PAM_MANIFEST_PATH) {
25
+ return resolve(process.env.PAM_MANIFEST_PATH);
26
+ }
27
+ return rootManifest;
28
+ }
29
+ export async function loadManifest() {
30
+ if (cachedManifest)
31
+ return cachedManifest;
32
+ const primaryPath = findManifestPath();
33
+ // Try primary path, then bundled fallback
34
+ const __dirname = dirname(fileURLToPath(import.meta.url));
35
+ const bundledPath = resolve(__dirname, "manifest.json");
36
+ for (const manifestPath of [primaryPath, bundledPath]) {
37
+ try {
38
+ const raw = await readFile(manifestPath, "utf-8");
39
+ cachedManifest = JSON.parse(raw);
40
+ return cachedManifest;
41
+ }
42
+ catch {
43
+ continue;
44
+ }
45
+ }
46
+ throw new Error("PAM manifest.json not found. Set PAM_PROJECT_ROOT to the PAM repo root, " +
47
+ "or PAM_MANIFEST_PATH to the manifest file location. " +
48
+ "Live tools (set_parameters, trigger_midi, etc.) work without the manifest.");
49
+ }
50
+ export async function getParameterIndex() {
51
+ if (paramIndex)
52
+ return paramIndex;
53
+ const manifest = await loadManifest();
54
+ paramIndex = new Map();
55
+ for (const p of manifest.parameters) {
56
+ paramIndex.set(p.paramId, p);
57
+ }
58
+ return paramIndex;
59
+ }
60
+ export async function getParametersByCell(cellId) {
61
+ const manifest = await loadManifest();
62
+ return manifest.parameters.filter((p) => p.cell === cellId);
63
+ }
64
+ export async function getGlobalParameters() {
65
+ const manifest = await loadManifest();
66
+ return manifest.parameters.filter((p) => !p.cell);
67
+ }
68
+ function getUserPresetsDir() {
69
+ if (process.platform === "win32") {
70
+ return resolve(process.env.APPDATA ?? homedir(), "MAP Audio", "PAM", "Presets");
71
+ }
72
+ return resolve(homedir(), "Library", "Application Support", "MAP Audio", "PAM", "Presets");
73
+ }
74
+ export async function findPresetDirs() {
75
+ const dirs = [];
76
+ const repoPresets = resolve(getProjectRoot(), "installer", "prepared-presets", "Presets");
77
+ try {
78
+ await stat(repoPresets);
79
+ dirs.push(repoPresets);
80
+ }
81
+ catch {
82
+ // not available
83
+ }
84
+ try {
85
+ const userPresets = getUserPresetsDir();
86
+ await stat(userPresets);
87
+ dirs.push(userPresets);
88
+ }
89
+ catch {
90
+ // not available
91
+ }
92
+ return dirs;
93
+ }
94
+ export async function resolvePresetPath(presetPath) {
95
+ const dirs = await findPresetDirs();
96
+ for (const dir of dirs) {
97
+ const candidate = resolve(dir, presetPath);
98
+ // Path traversal guard: resolved path must stay within the allowed directory
99
+ if (!candidate.startsWith(dir + sep) && candidate !== dir)
100
+ continue;
101
+ try {
102
+ await stat(candidate);
103
+ return candidate;
104
+ }
105
+ catch {
106
+ continue;
107
+ }
108
+ }
109
+ throw new Error(`Preset not found: ${presetPath}`);
110
+ }
111
+ export async function walkPresets(dir, prefix = "") {
112
+ const results = [];
113
+ try {
114
+ const entries = await readdir(dir, { withFileTypes: true });
115
+ for (const entry of entries) {
116
+ const fullPath = join(dir, entry.name);
117
+ const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
118
+ if (entry.isDirectory()) {
119
+ results.push(...(await walkPresets(fullPath, relPath)));
120
+ }
121
+ else if (extname(entry.name) === ".preset") {
122
+ results.push({
123
+ path: fullPath,
124
+ name: basename(entry.name, ".preset"),
125
+ relativePath: relPath,
126
+ });
127
+ }
128
+ }
129
+ }
130
+ catch {
131
+ // directory not readable
132
+ }
133
+ return results;
134
+ }
135
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"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"}