godmode 0.0.2 → 0.0.3
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/{add-AJERMPEA.js → add-D74TIN3J.js} +12 -11
- package/dist/{chunk-EXWYJU2I.js → chunk-3AQ2CZKC.js} +5 -4
- package/dist/chunk-4EU5JX6C.js +160 -0
- package/dist/{chunk-GJCJO7YT.js → chunk-AWGLXW3B.js} +24 -11
- package/dist/{chunk-2J2VDCQ4.js → chunk-KHFZI7IA.js} +380 -138
- package/dist/chunk-SMQBMG24.js +102 -0
- package/dist/chunk-XFFBFBN6.js +16 -0
- package/dist/chunk-YDXTIN53.js +212 -0
- package/dist/chunk-ZTPILA7B.js +403 -0
- package/dist/dist-I5CEQ4AC.js +779 -0
- package/dist/ext-K4XEAZGS.js +102 -0
- package/dist/index.js +262 -1540
- package/dist/permissions-E4G66OUZ.js +125 -0
- package/dist/{prompt-SWOWWAOH.js → prompt-YPAZ5433.js} +12 -6
- package/dist/{server-4YVOAJJG.js → server-L3L4TKK3.js} +19 -2
- package/dist/settings-3FF5WWEY.js +17 -0
- package/package.json +10 -9
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
explainPermission,
|
|
4
|
+
matchRoute,
|
|
5
|
+
resourceFromRawPath,
|
|
6
|
+
resourceFromSegments,
|
|
7
|
+
suggestedAllowRule
|
|
8
|
+
} from "./chunk-4EU5JX6C.js";
|
|
9
|
+
import {
|
|
10
|
+
EXIT_CODES
|
|
11
|
+
} from "./chunk-XFFBFBN6.js";
|
|
12
|
+
import {
|
|
13
|
+
warnSettingsErrors
|
|
14
|
+
} from "./chunk-SMQBMG24.js";
|
|
15
|
+
import {
|
|
16
|
+
loadManifest
|
|
17
|
+
} from "./chunk-KHFZI7IA.js";
|
|
18
|
+
import "./chunk-3AQ2CZKC.js";
|
|
19
|
+
import "./chunk-YDXTIN53.js";
|
|
20
|
+
|
|
21
|
+
// src/commands/permissions.ts
|
|
22
|
+
function showPermissionsHelp() {
|
|
23
|
+
console.log(`Inspect godmode permission policy.
|
|
24
|
+
|
|
25
|
+
Usage:
|
|
26
|
+
godmode permissions list
|
|
27
|
+
godmode permissions explain <extension> <interface> <target> [method]
|
|
28
|
+
|
|
29
|
+
Exit codes:
|
|
30
|
+
0 success / allowed
|
|
31
|
+
2 usage or parse error
|
|
32
|
+
3 extension or route not found
|
|
33
|
+
4 permission denied
|
|
34
|
+
10 upstream HTTP 4xx
|
|
35
|
+
11 upstream HTTP 5xx
|
|
36
|
+
12 upstream failure`);
|
|
37
|
+
}
|
|
38
|
+
async function runPermissions(rest) {
|
|
39
|
+
const cmd = rest[0];
|
|
40
|
+
if (!cmd || cmd === "--help" || cmd === "-h") {
|
|
41
|
+
warnSettingsErrors();
|
|
42
|
+
showPermissionsHelp();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (cmd === "list") {
|
|
46
|
+
const { loadSettingsDetailed } = await import("./settings-3FF5WWEY.js");
|
|
47
|
+
const loaded = loadSettingsDetailed();
|
|
48
|
+
for (const error of loaded.errors) {
|
|
49
|
+
process.stderr.write(`Warning: cannot parse ${error.path}: ${error.message}
|
|
50
|
+
`);
|
|
51
|
+
}
|
|
52
|
+
const sources = loaded.sources.flatMap((source) => {
|
|
53
|
+
const extensions = source.settings.extensions ?? {};
|
|
54
|
+
return Object.entries(extensions).flatMap(([slug, settings]) => {
|
|
55
|
+
const permissions = settings.permissions;
|
|
56
|
+
if (!permissions?.allow?.length && !permissions?.deny?.length) return [];
|
|
57
|
+
return [{ slug, permissions, origin: `${source.scope}:${source.path}` }];
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
if (!sources.length) {
|
|
61
|
+
console.log("No permissions configured.");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
for (const { slug, permissions, origin } of sources) {
|
|
65
|
+
console.log(`${slug}:`);
|
|
66
|
+
for (const effect of ["allow", "deny"]) {
|
|
67
|
+
for (const rule of permissions[effect] ?? []) {
|
|
68
|
+
const resources = rule.resources?.join(", ") || "*";
|
|
69
|
+
const methods = rule.methods?.join(", ") || "*";
|
|
70
|
+
console.log(` ${effect} resources=[${resources}] methods=[${methods}] origin=${origin}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (cmd === "explain") {
|
|
77
|
+
const [extension, iface, target, maybeMethod] = rest.slice(1);
|
|
78
|
+
if (!extension || !iface || !target) {
|
|
79
|
+
process.stderr.write("Usage: godmode permissions explain <extension> <interface> <target> [method]\n");
|
|
80
|
+
process.exit(EXIT_CODES.usage);
|
|
81
|
+
}
|
|
82
|
+
const method = iface === "mcp" ? "mcp" : maybeMethod || "GET";
|
|
83
|
+
const resource = await explainResource(extension, iface, target, method);
|
|
84
|
+
const decision = explainPermission({ extension, resource, method });
|
|
85
|
+
console.log(`${decision.allowed ? "allow" : "deny"} ${extension} ${iface} ${target} ${method.toUpperCase()}`);
|
|
86
|
+
if (decision.reason) console.log(decision.reason);
|
|
87
|
+
if (decision.rule) {
|
|
88
|
+
console.log(`winning rule: resources=[${decision.rule.resources?.join(", ") || "*"}] methods=[${decision.rule.methods?.join(", ") || "*"}] origin=${decision.origin || "settings.yaml"}`);
|
|
89
|
+
}
|
|
90
|
+
if (!decision.allowed) {
|
|
91
|
+
console.log(`suggested allow rule:
|
|
92
|
+
${suggestedAllowRule({ extension, resource, method })}`);
|
|
93
|
+
process.exit(EXIT_CODES.permissionDenied);
|
|
94
|
+
}
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
process.stderr.write(`Unknown permissions command '${cmd}'.
|
|
98
|
+
`);
|
|
99
|
+
process.stderr.write(`Try 'godmode permissions --help' for more information.
|
|
100
|
+
`);
|
|
101
|
+
process.exit(EXIT_CODES.usage);
|
|
102
|
+
}
|
|
103
|
+
async function explainResource(extension, iface, target, method) {
|
|
104
|
+
if (iface === "mcp") return target;
|
|
105
|
+
if (iface === "api" || iface === "graphql") {
|
|
106
|
+
try {
|
|
107
|
+
const manifest = await loadManifest(extension, iface);
|
|
108
|
+
const normalizedMethod = method.toLowerCase();
|
|
109
|
+
if (target.startsWith("/")) {
|
|
110
|
+
const exact = manifest.routes.find((route) => route.method === normalizedMethod && route.path === target);
|
|
111
|
+
if (exact) return resourceFromSegments(exact.segments);
|
|
112
|
+
}
|
|
113
|
+
const segments = target.replace(/^\/+/, "").split(/[/.]/).filter(Boolean);
|
|
114
|
+
const match = matchRoute(manifest, segments, normalizedMethod);
|
|
115
|
+
if (match) return resourceFromSegments(match.route.segments);
|
|
116
|
+
return resourceFromRawPath(target);
|
|
117
|
+
} catch {
|
|
118
|
+
return target.replace(/^\/+/, "").replace(/\//g, ".").replace(/^v\d+\./i, "") || "*";
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return target.replace(/^\/+/, "").replace(/\//g, ".").replace(/^v\d+\./i, "") || "*";
|
|
122
|
+
}
|
|
123
|
+
export {
|
|
124
|
+
runPermissions
|
|
125
|
+
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/prompt.ts
|
|
4
4
|
import prompts from "prompts";
|
|
5
|
-
import { writeFileSync } from "fs";
|
|
5
|
+
import { mkdirSync, writeFileSync } from "fs";
|
|
6
6
|
import { resolve } from "path";
|
|
7
7
|
import { stringify } from "yaml";
|
|
8
8
|
async function configWizard() {
|
|
@@ -60,11 +60,15 @@ async function configWizard() {
|
|
|
60
60
|
} });
|
|
61
61
|
const config = {
|
|
62
62
|
name: response.name.charAt(0).toUpperCase() + response.name.slice(1),
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
slug: response.name.toLowerCase().replace(/[^a-z0-9-]/g, "-"),
|
|
64
|
+
interfaces: {
|
|
65
|
+
api: {
|
|
66
|
+
spec: response.spec
|
|
67
|
+
}
|
|
68
|
+
}
|
|
65
69
|
};
|
|
66
70
|
if (response.description) config.description = response.description;
|
|
67
|
-
if (response.url) config.url = response.url;
|
|
71
|
+
if (response.url) config.interfaces.api.url = response.url;
|
|
68
72
|
if (response.envVar) {
|
|
69
73
|
config.auth = { env: response.envVar };
|
|
70
74
|
if (response.authType && response.authType !== "bearer") {
|
|
@@ -72,7 +76,8 @@ async function configWizard() {
|
|
|
72
76
|
}
|
|
73
77
|
}
|
|
74
78
|
const yaml = stringify(config);
|
|
75
|
-
const
|
|
79
|
+
const dirname = response.name.toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
80
|
+
const filename = `${dirname}/manifest.yaml`;
|
|
76
81
|
console.log(`
|
|
77
82
|
\x1B[2m${yaml}\x1B[0m`);
|
|
78
83
|
const { write } = await prompts({
|
|
@@ -86,10 +91,11 @@ async function configWizard() {
|
|
|
86
91
|
return;
|
|
87
92
|
}
|
|
88
93
|
const filepath = resolve(process.cwd(), filename);
|
|
94
|
+
mkdirSync(resolve(process.cwd(), dirname), { recursive: true });
|
|
89
95
|
writeFileSync(filepath, yaml);
|
|
90
96
|
console.log(`
|
|
91
97
|
Saved ${filepath}`);
|
|
92
|
-
console.log(`Run \x1B[1mgodmode
|
|
98
|
+
console.log(`Run \x1B[1mgodmode ext install ./${dirname}\x1B[0m to register it.`);
|
|
93
99
|
}
|
|
94
100
|
export {
|
|
95
101
|
configWizard
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
executeToString
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-AWGLXW3B.js";
|
|
5
5
|
import {
|
|
6
6
|
executeMcpTool
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-3AQ2CZKC.js";
|
|
8
|
+
import "./chunk-YDXTIN53.js";
|
|
8
9
|
|
|
9
10
|
// ../../interfaces/mcp/src/server.ts
|
|
10
11
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -98,6 +99,15 @@ async function serveMcp(manifest, options = {}) {
|
|
|
98
99
|
}
|
|
99
100
|
try {
|
|
100
101
|
let result;
|
|
102
|
+
const extension = manifest.config.slug || manifest.name;
|
|
103
|
+
const resource = type === "mcp" ? resourceFromTool(toolName) : resourceFromSegments(tool.route.segments);
|
|
104
|
+
const method = type === "mcp" ? "mcp" : tool.route.method;
|
|
105
|
+
if (options.checkPermission) {
|
|
106
|
+
const check = options.checkPermission({ extension, resource, method });
|
|
107
|
+
if (!check.allowed) {
|
|
108
|
+
return { content: [{ type: "text", text: `Blocked: ${check.reason}` }], isError: true };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
101
111
|
if (type === "mcp") {
|
|
102
112
|
result = await executeMcpTool(manifest.config, toolName, args, {});
|
|
103
113
|
} else if (type === "graphql") {
|
|
@@ -124,6 +134,13 @@ async function serveMcp(manifest, options = {}) {
|
|
|
124
134
|
});
|
|
125
135
|
await server.connect(new StdioServerTransport());
|
|
126
136
|
}
|
|
137
|
+
function resourceFromSegments(segments) {
|
|
138
|
+
const parts = segments.filter((s) => !s.isParam).map((s) => s.value).filter((v) => !/^v\d+$/i.test(v));
|
|
139
|
+
return parts.join(".") || "*";
|
|
140
|
+
}
|
|
141
|
+
function resourceFromTool(toolName) {
|
|
142
|
+
return toolName;
|
|
143
|
+
}
|
|
127
144
|
export {
|
|
128
145
|
serveMcp
|
|
129
146
|
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
extensionSettings,
|
|
4
|
+
loadSettings,
|
|
5
|
+
loadSettingsDetailed,
|
|
6
|
+
resetSettingsCache,
|
|
7
|
+
settingsErrorMessage,
|
|
8
|
+
warnSettingsErrors
|
|
9
|
+
} from "./chunk-SMQBMG24.js";
|
|
10
|
+
export {
|
|
11
|
+
extensionSettings,
|
|
12
|
+
loadSettings,
|
|
13
|
+
loadSettingsDetailed,
|
|
14
|
+
resetSettingsCache,
|
|
15
|
+
settingsErrorMessage,
|
|
16
|
+
warnSettingsErrors
|
|
17
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "godmode",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "any API as CLI. any API as MCP. zero code generation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"exports": {
|
|
27
27
|
"./spec": "./src/spec.ts",
|
|
28
28
|
"./config": "./src/config.ts",
|
|
29
|
+
"./permissions": "./src/permissions.ts",
|
|
29
30
|
"./help": "./src/help.ts"
|
|
30
31
|
},
|
|
31
32
|
"files": [
|
|
@@ -34,23 +35,23 @@
|
|
|
34
35
|
"README.md"
|
|
35
36
|
],
|
|
36
37
|
"dependencies": {
|
|
37
|
-
"@hey-api/openapi-ts": "^0.
|
|
38
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
38
|
+
"@hey-api/openapi-ts": "^0.97.0",
|
|
39
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
39
40
|
"fuzzysort": "^3.1.0",
|
|
40
41
|
"prompts": "^2.4.2",
|
|
41
|
-
"yaml": "^2.8.
|
|
42
|
+
"yaml": "^2.8.4",
|
|
42
43
|
"@godmode-cli/cli": "0.0.1",
|
|
43
44
|
"@godmode-cli/command-agent": "0.0.1",
|
|
44
|
-
"@godmode-cli/interface-graphql": "0.0.1",
|
|
45
45
|
"@godmode-cli/interface-api": "0.0.1",
|
|
46
|
+
"@godmode-cli/interface-graphql": "0.0.1",
|
|
46
47
|
"@godmode-cli/interface-mcp": "0.0.1"
|
|
47
48
|
},
|
|
48
49
|
"devDependencies": {
|
|
49
|
-
"@types/node": "^
|
|
50
|
+
"@types/node": "^25.6.0",
|
|
50
51
|
"@types/prompts": "^2.4.9",
|
|
51
|
-
"tsup": "^8.
|
|
52
|
-
"typescript": "^
|
|
53
|
-
"vitest": "^4.1.
|
|
52
|
+
"tsup": "^8.5.1",
|
|
53
|
+
"typescript": "^6.0.3",
|
|
54
|
+
"vitest": "^4.1.5"
|
|
54
55
|
},
|
|
55
56
|
"engines": {
|
|
56
57
|
"node": ">=20.0.0"
|