create-guardio 0.0.7 → 0.0.9

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.
Files changed (2) hide show
  1. package/index.mjs +117 -45
  2. package/package.json +5 -2
package/index.mjs CHANGED
@@ -1,11 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
  import { createInterface } from "node:readline";
3
- import { mkdir, writeFile } from "node:fs/promises";
4
- import { resolve, dirname } from "node:path";
3
+ import { mkdir, writeFile, mkdtemp } from "node:fs/promises";
4
+ import { createWriteStream } from "node:fs";
5
+ import { readdirSync, cpSync, existsSync } from "node:fs";
6
+ import { resolve, dirname, join } from "node:path";
5
7
  import { fileURLToPath } from "node:url";
8
+ import { pipeline } from "node:stream/promises";
9
+ import { tmpdir } from "node:os";
10
+ import { spawnSync } from "node:child_process";
11
+ import decompress from "decompress";
6
12
 
7
13
  const __dirname = dirname(fileURLToPath(import.meta.url));
8
14
 
15
+ const DASHBOARD_REPO =
16
+ process.env.GUARDIO_DASHBOARD_REPO || "radoslaw-sz/guardio";
17
+ const DASHBOARD_BRANCH = process.env.GUARDIO_DASHBOARD_BRANCH || "main";
18
+ const DASHBOARD_TARBALL_URL = `https://github.com/${DASHBOARD_REPO}/archive/refs/heads/${DASHBOARD_BRANCH}.tar.gz`;
19
+
9
20
  const rl = createInterface({ input: process.stdin, output: process.stdout });
10
21
 
11
22
  function ask(question, defaultAnswer = "") {
@@ -27,46 +38,83 @@ function askYesNo(question, defaultAnswer = "n") {
27
38
  );
28
39
  }
29
40
 
41
+ async function setupDashboardFromTarball(guardioPath) {
42
+ const dashboardPath = join(guardioPath, "dashboard");
43
+ console.log("Downloading dashboard from GitHub...");
44
+ let res;
45
+ try {
46
+ res = await fetch(DASHBOARD_TARBALL_URL, { redirect: "follow" });
47
+ } catch (err) {
48
+ console.error(
49
+ "Could not download dashboard template. Check network and GUARDIO_DASHBOARD_REPO.",
50
+ );
51
+ throw err;
52
+ }
53
+ if (!res.ok) {
54
+ throw new Error(
55
+ `Dashboard download failed: ${res.status} ${res.statusText}. Check repo (${DASHBOARD_REPO}) and branch (${DASHBOARD_BRANCH}).`,
56
+ );
57
+ }
58
+ const tmpDir = await mkdtemp(join(tmpdir(), "guardio-dashboard-"));
59
+ const tarPath = join(tmpDir, "archive.tar.gz");
60
+ const w = createWriteStream(tarPath);
61
+ await pipeline(res.body, w);
62
+ console.log("Extracting...");
63
+ const extractDir = join(tmpDir, "extract");
64
+ await mkdir(extractDir, { recursive: true });
65
+ await decompress(tarPath, extractDir);
66
+ const topLevel = readdirSync(extractDir)[0];
67
+ if (!topLevel) {
68
+ throw new Error("Dashboard tarball had no top-level directory.");
69
+ }
70
+ const srcDashboard = join(extractDir, topLevel, "packages", "dashboard");
71
+ if (!existsSync(srcDashboard)) {
72
+ throw new Error(
73
+ "Dashboard tarball missing packages/dashboard. Wrong repo or branch?",
74
+ );
75
+ }
76
+ await mkdir(dashboardPath, { recursive: true });
77
+ cpSync(srcDashboard, dashboardPath, { recursive: true });
78
+ console.log("Installing dashboard dependencies...");
79
+ const installResult = spawnSync(
80
+ "pnpm",
81
+ ["install"],
82
+ { cwd: dashboardPath, stdio: "inherit", shell: true },
83
+ );
84
+ if (installResult.status !== 0) {
85
+ const npmResult = spawnSync(
86
+ "npm",
87
+ ["install"],
88
+ { cwd: dashboardPath, stdio: "inherit", shell: true },
89
+ );
90
+ if (npmResult.status !== 0) {
91
+ throw new Error("Failed to install dashboard dependencies (tried pnpm and npm).");
92
+ }
93
+ }
94
+ }
95
+
30
96
  async function main() {
31
97
  console.log("\nCreate Guardio\n");
32
98
 
33
- const guardioDir = await ask("Guardio directory", "./mcp-server-gated");
99
+ const guardioDir = await ask("Guardio directory", "guardio-project");
34
100
  const guardioPath = resolve(process.cwd(), guardioDir);
35
101
 
36
- // Servers: at least one, with name + URL
37
- const servers = [];
38
- let serverName = await ask("First MCP server name", "default");
39
- let serverUrl = await ask("MCP server URL", "https://example.com/mcp");
40
- servers.push({ name: serverName, type: "url", url: serverUrl });
41
-
42
- while (await askYesNo("Add another MCP server?", "n")) {
43
- serverName = await ask("MCP server name", "server-" + (servers.length + 1));
44
- serverUrl = await ask("MCP server URL", "https://example.com/mcp");
45
- servers.push({ name: serverName, type: "url", url: serverUrl });
46
- }
47
-
48
102
  const portStr = await ask("Guardio HTTP server port", "3939");
49
103
  const port = parseInt(portStr, 10) || 3939;
50
104
 
51
- const installDashboard = await askYesNo("Install dashboard?", "n");
105
+ const useStorage = await askYesNo(
106
+ "Use storage and events (for dashboard / policy state)?",
107
+ "n",
108
+ );
109
+
110
+ // All built-in policy plugins by default
111
+ const plugins = [
112
+ { type: "policy", name: "deny-tool-access" },
113
+ { type: "policy", name: "deny-regex-parameter" },
114
+ ];
52
115
 
53
- // Built-in plugins: by category
54
- console.log("\nPolicy plugins:");
55
- console.log(" 1) none");
56
- console.log(" 2) deny-tool-access");
57
- console.log(" 3) regex");
58
- console.log(" 4) both");
59
- const policyChoice = await ask("Policy plugins (1-4)", "1");
60
- const policyNum = parseInt(policyChoice, 10) || 1;
61
- const policyPlugins = [];
62
- if (policyNum === 2 || policyNum === 4) policyPlugins.push({ type: "policy", name: "deny-tool-access" });
63
- if (policyNum === 3 || policyNum === 4) policyPlugins.push({ type: "policy", name: "regex" });
64
-
65
- const useStorage = await askYesNo("Use storage and events (for dashboard / policy state)?", installDashboard ? "y" : "n");
66
-
67
- const plugins = [...policyPlugins];
68
116
  if (useStorage) {
69
- console.log(" 1) sqlite (local file)");
117
+ console.log(" 1) sqlite (in-memory or file)");
70
118
  console.log(" 2) postgres");
71
119
  const storageBackend = await ask("Storage backend (1-2)", "1");
72
120
  if (storageBackend === "2") {
@@ -79,20 +127,35 @@ async function main() {
79
127
  plugins.push({ type: "eventSink", name: "postgres" });
80
128
  plugins.push({ type: "eventSinkStore", name: "postgres" });
81
129
  } else {
82
- const sqliteConfig = { database: "guardio.sqlite" };
130
+ const sqliteInMemory = await askYesNo(
131
+ "Use in-memory SQLite? (y = in-memory, n = file guardio.sqlite)",
132
+ "y",
133
+ );
134
+ const sqliteConfig = sqliteInMemory
135
+ ? { inMemory: true }
136
+ : { database: "guardio.sqlite" };
83
137
  plugins.push({ type: "storage", name: "sqlite", config: sqliteConfig });
84
138
  plugins.push({ type: "eventSink", name: "sqlite", config: sqliteConfig });
85
- plugins.push({ type: "eventSinkStore", name: "sqlite", config: sqliteConfig });
139
+ plugins.push({
140
+ type: "eventSinkStore",
141
+ name: "sqlite",
142
+ config: sqliteConfig,
143
+ });
86
144
  }
87
145
  }
88
146
 
89
- const addExamplePlugin = await askYesNo("Add example custom policy plugin?", "n");
147
+ const addExamplePlugin = await askYesNo(
148
+ "Add example custom policy plugin?",
149
+ "n",
150
+ );
90
151
  if (addExamplePlugin) {
91
152
  plugins.push({ type: "policy", name: "example", path: "./plugins/example" });
92
153
  }
93
154
 
155
+ const installDashboard = await askYesNo("Install dashboard?", "n");
156
+
94
157
  const config = {
95
- servers,
158
+ servers: [],
96
159
  client: { port, host: "127.0.0.1" },
97
160
  plugins,
98
161
  };
@@ -117,8 +180,7 @@ async function main() {
117
180
  },
118
181
  };
119
182
  if (installDashboard) {
120
- packageJson.dependencies["@guardiojs/dashboard"] = "*";
121
- packageJson.scripts.dashboard = "guardio-dashboard";
183
+ packageJson.scripts.dashboard = "cd dashboard && npm run dev";
122
184
  }
123
185
  await writeFile(
124
186
  resolve(guardioPath, "package.json"),
@@ -126,6 +188,10 @@ async function main() {
126
188
  "utf-8",
127
189
  );
128
190
 
191
+ if (installDashboard) {
192
+ await setupDashboardFromTarball(guardioPath);
193
+ }
194
+
129
195
  const tsconfig = {
130
196
  compilerOptions: {
131
197
  target: "ES2022",
@@ -145,6 +211,9 @@ async function main() {
145
211
 
146
212
  const configTsContent = `import type { GuardioConfig } from "@guardiojs/guardio";
147
213
 
214
+ // Example server (uncomment and add to servers array to proxy an MCP server):
215
+ // { name: "nuvei-docs", type: "url", url: "https://mcp.nuvei.com/sse" }
216
+
148
217
  const config: GuardioConfig = ${JSON.stringify(config, null, 2).replace(/^/gm, " ")};
149
218
 
150
219
  export default config;
@@ -171,9 +240,9 @@ export default config;
171
240
  class ExamplePolicyPlugin implements PolicyPluginInterface {
172
241
  readonly name = "example";
173
242
 
174
- evaluate(context: PolicyRequestContext): PolicyResult {
243
+ async evaluate(context: PolicyRequestContext): Promise<PolicyResult> {
175
244
  // Example: allow all calls. Replace with your policy logic.
176
- return { verdict: "allow" };
245
+ return Promise.resolve({ verdict: "allow" });
177
246
  }
178
247
  }
179
248
 
@@ -192,7 +261,7 @@ export default new ExamplePolicyPlugin();
192
261
  Add a plugin by setting \`path\` in \`guardio.config.ts\` to a directory that contains \`index.js\` or \`index.mjs\` (compile from \`index.ts\` with \`npm run build\`).
193
262
 
194
263
  - The directory must have \`index.js\` or \`index.mjs\` whose **default export is the plugin instance** (no descriptor).
195
- - Policy: implement \`PolicyPluginInterface\` (name, evaluate). Config: \`{ "type": "policy", "name": "my-policy", "path": "./plugins/my-policy" }\`.
264
+ - Policy: implement \`PolicyPluginInterface\` (name, evaluate returning Promise<PolicyResult>). Config: \`{ "type": "policy", "name": "my-policy", "path": "./plugins/my-policy" }\`.
196
265
 
197
266
  Import types from "@guardiojs/guardio".${addExamplePlugin ? " See example/ for a policy plugin." : ""}
198
267
  `;
@@ -204,7 +273,10 @@ Import types from "@guardiojs/guardio".${addExamplePlugin ? " See example/ for a
204
273
 
205
274
  console.log("\n---\n");
206
275
  console.log("Next steps");
207
- console.log(" cd " + guardioDir + " && npm install");
276
+ console.log(" cd " + guardioDir);
277
+ console.log(
278
+ " npm install # or: pnpm install, yarn, bun install, etc.",
279
+ );
208
280
  if (addExamplePlugin) {
209
281
  console.log(" npm run build # compile plugins (index.ts → index.js)");
210
282
  }
@@ -213,9 +285,9 @@ Import types from "@guardiojs/guardio".${addExamplePlugin ? " See example/ for a
213
285
  console.log(" npm run guardio");
214
286
  console.log("");
215
287
  if (installDashboard) {
216
- console.log("Run dashboard (point it at Guardio URL):");
217
- console.log(" npm run dashboard");
218
- console.log(" # Dashboard needs Guardio base URL, e.g. http://127.0.0.1:" + port);
288
+ console.log("Run dashboard (standalone copy from GitHub; point at Guardio URL):");
289
+ console.log(" pnpm run dashboard # or: npm run dashboard");
290
+ console.log(" # Guardio base URL: http://127.0.0.1:" + port);
219
291
  console.log("");
220
292
  }
221
293
  console.log("Add to MCP client (use URL):");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-guardio",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "Scaffold a Guardio project",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,5 +18,8 @@
18
18
  "agents"
19
19
  ],
20
20
  "author": "Radoslaw Szymkiewicz",
21
- "license": "Apache-2.0"
21
+ "license": "Apache-2.0",
22
+ "dependencies": {
23
+ "decompress": "^4.2.1"
24
+ }
22
25
  }