godmode 0.0.1 → 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/README.md ADDED
@@ -0,0 +1,34 @@
1
+ # godmode
2
+
3
+ any API as CLI. any API as MCP. zero code generation.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ npm install -g godmode
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```sh
14
+ godmode ext install stripe
15
+ godmode stripe api customers cus_123
16
+ godmode stripe mcp # serve as MCP server
17
+ ```
18
+
19
+ Run `godmode --help` or `godmode <extension> --help` at any nesting level for usage.
20
+
21
+ ## Claude Code
22
+
23
+ ```json
24
+ {
25
+ "mcpServers": {
26
+ "stripe": {
27
+ "command": "godmode",
28
+ "args": ["stripe", "mcp"]
29
+ }
30
+ }
31
+ }
32
+ ```
33
+
34
+ See [documentation](https://docs.godmode.so) for more.
@@ -1,20 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  addApi
4
- } from "./chunk-FBMQ3AC4.js";
5
- import "./chunk-EHS56XXY.js";
4
+ } from "./chunk-KHFZI7IA.js";
5
+ import "./chunk-3AQ2CZKC.js";
6
+ import "./chunk-YDXTIN53.js";
6
7
 
7
8
  // src/commands/add.ts
8
- async function runAdd(args) {
9
+ async function runAdd(args, scope = "project") {
9
10
  if (!args[0] || args[0] === "--help" || args[0] === "-h") {
10
- console.log(`Add an adapter from a folder, built-in name, or manifest.
11
+ console.log(`Add an extension from a folder, built-in name, or manifest.
11
12
 
12
13
  Usage:
13
- godmode add <name> Built-in adapter (e.g. stripe, github)
14
- godmode add <folder> Folder containing manifest.yaml
14
+ godmode ext install <name> Built-in extension (e.g. stripe, github)
15
+ godmode ext install <folder> Folder containing manifest.yaml
16
+ godmode ext install <manifest> Direct path to manifest.yaml/json
15
17
 
16
18
  Manifest format (manifest.yaml):
17
- slug: stripe CLI name (used as "godmode <slug>")
19
+ slug: stripe CLI name
18
20
  name: Stripe Display name
19
21
  description: Payments API Short description
20
22
  type: api api | graphql | mcp
@@ -32,12 +34,13 @@ Types:
32
34
  mcp MCP endpoint (requires url)
33
35
 
34
36
  Examples:
35
- $ godmode add stripe
36
- $ godmode add ./my-adapter
37
- $ godmode add openai`);
37
+ $ godmode ext install stripe
38
+ $ godmode ext install ./my-extension
39
+ $ godmode ext install ./my-extension/manifest.yaml
40
+ $ godmode ext install openai`);
38
41
  process.exit(args[0] ? 0 : 1);
39
42
  }
40
- await addApi(args[0]);
43
+ await addApi(args[0], scope);
41
44
  }
42
45
  export {
43
46
  runAdd
@@ -1,6 +1,9 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ AuthStrategy
4
+ } from "./chunk-YDXTIN53.js";
2
5
 
3
- // src/protocols/mcp.ts
6
+ // ../../interfaces/mcp/src/index.ts
4
7
  var requestId = 0;
5
8
  async function mcpRequest(url, method, params, headers, sessionId) {
6
9
  const body = {
@@ -53,9 +56,7 @@ function buildAuthHeaders(config) {
53
56
  const headers = { ...config.headers };
54
57
  const token = config.auth?.env ? process.env[config.auth.env] : void 0;
55
58
  if (token) {
56
- const authType = config.auth?.type || "bearer";
57
- if (authType === "bearer") headers["Authorization"] = `Bearer ${token}`;
58
- else if (authType === "api-key") headers[config.auth?.header || "X-API-Key"] = token;
59
+ AuthStrategy.for(config.auth).apply(headers, token);
59
60
  }
60
61
  return headers;
61
62
  }
@@ -120,7 +121,7 @@ function validateMcpFlags(method, query) {
120
121
  }
121
122
  async function executeMcpTool(config, toolName, args, options) {
122
123
  const headers = buildAuthHeaders(config);
123
- if (options.dryRun || options.verbose) {
124
+ if (options.dryRun || options.debug) {
124
125
  process.stderr.write(`CALL ${config.url} \u2192 ${toolName}
125
126
  `);
126
127
  if (Object.keys(args).length) {
@@ -0,0 +1,160 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ loadSettingsDetailed,
4
+ settingsErrorMessage
5
+ } from "./chunk-SMQBMG24.js";
6
+
7
+ // src/permissions.ts
8
+ function checkPermission(input) {
9
+ const loaded = loadSettingsDetailed();
10
+ const settingsError = loaded.errors[0];
11
+ if (settingsError) {
12
+ return {
13
+ allowed: false,
14
+ reason: settingsErrorMessage(settingsError),
15
+ defaultDenied: true
16
+ };
17
+ }
18
+ const blocks = loaded.sources.map((source) => ({
19
+ origin: `${source.scope}:${source.path}`,
20
+ permissions: source.settings.extensions?.[input.extension]?.permissions
21
+ })).filter((source) => !!source.permissions);
22
+ const hasAny = blocks.some(({ permissions }) => !!(permissions?.allow?.length || permissions?.deny?.length));
23
+ if (!hasAny) return { allowed: true };
24
+ for (const block of blocks) {
25
+ for (const rule of block.permissions?.deny ?? []) {
26
+ if (ruleMatches(rule, input)) {
27
+ return {
28
+ allowed: false,
29
+ reason: `denied by permissions for ${input.extension}: ${describe(rule, input, "deny")}`,
30
+ rule,
31
+ origin: block.origin
32
+ };
33
+ }
34
+ }
35
+ }
36
+ for (const block of blocks) {
37
+ for (const rule of block.permissions?.allow ?? []) {
38
+ if (ruleMatches(rule, input)) return { allowed: true, rule, origin: block.origin };
39
+ }
40
+ }
41
+ return {
42
+ allowed: false,
43
+ reason: `no allow rule matches ${input.resource} ${input.method.toUpperCase()} for ${input.extension}`,
44
+ defaultDenied: true
45
+ };
46
+ }
47
+ function explainPermission(input) {
48
+ return { ...checkPermission(input), input };
49
+ }
50
+ function suggestedAllowRule(input) {
51
+ return [
52
+ "permissions:",
53
+ " allow:",
54
+ ` - resources: [${input.resource}]`,
55
+ ` methods: [${input.method.toUpperCase()}]`
56
+ ].join("\n");
57
+ }
58
+ function ruleMatches(rule, input) {
59
+ const resources = rule.resources ?? ["*"];
60
+ const resourceOk = resources.some((pattern) => matchResource(pattern, input.resource));
61
+ if (!resourceOk) return false;
62
+ if (!rule.methods || rule.methods.length === 0) return true;
63
+ return rule.methods.some((m) => m.toLowerCase() === input.method.toLowerCase());
64
+ }
65
+ function matchResource(pattern, resource) {
66
+ if (pattern === "*" || pattern === resource) return true;
67
+ const pp = pattern.split(".");
68
+ const rp = resource.split(".");
69
+ if (pp[pp.length - 1] === "*" && rp.length >= pp.length) {
70
+ for (let i = 0; i < pp.length - 1; i++) {
71
+ if (pp[i] !== "*" && pp[i] !== rp[i]) return false;
72
+ }
73
+ return true;
74
+ }
75
+ if (pp.length !== rp.length) return false;
76
+ for (let i = 0; i < pp.length; i++) {
77
+ if (pp[i] !== "*" && pp[i] !== rp[i]) return false;
78
+ }
79
+ return true;
80
+ }
81
+ function describe(rule, input, effect) {
82
+ const res = (rule.resources ?? ["*"]).join(", ");
83
+ const mth = rule.methods?.join(", ") || "any method";
84
+ return `${effect} ${res} [${mth}] while acting on ${input.resource} ${input.method.toUpperCase()}`;
85
+ }
86
+ function resourceFromSegments(segments) {
87
+ const parts = segments.filter((s) => !s.isParam).map((s) => s.value).filter((v) => !/^v\d+$/i.test(v));
88
+ return parts.join(".") || "*";
89
+ }
90
+ function resourceFromTool(toolName) {
91
+ return toolName;
92
+ }
93
+ function resourceFromRawPath(path) {
94
+ const parts = path.split("/").filter(Boolean).filter((v) => !/^v\d+$/i.test(v));
95
+ return parts.join(".") || "*";
96
+ }
97
+
98
+ // ../../interfaces/api/src/match.ts
99
+ function matchRoute(manifest, userSegments, method) {
100
+ if (userSegments.length > 0) {
101
+ const ver = manifest.versions.find((v) => v.name === userSegments[0]);
102
+ if (ver) {
103
+ const match = findMatch(manifest.routes, userSegments.slice(1), method, ver.name);
104
+ if (match) return match;
105
+ }
106
+ }
107
+ return findMatch(manifest.routes, userSegments, method);
108
+ }
109
+ function findMatch(routes, userSegments, method, version) {
110
+ let bestMatch = null;
111
+ let bestScore = -1;
112
+ for (const route of routes) {
113
+ if (route.method !== method) continue;
114
+ if (route.segments.length !== userSegments.length) continue;
115
+ if (version !== void 0 && route.version !== version) continue;
116
+ let score = 0;
117
+ const params = {};
118
+ let matches = true;
119
+ for (let i = 0; i < route.segments.length; i++) {
120
+ const seg = route.segments[i];
121
+ const userSeg = userSegments[i];
122
+ if (seg.isParam) {
123
+ params[seg.value] = userSeg;
124
+ } else if (seg.value === userSeg) {
125
+ score += 1;
126
+ } else {
127
+ matches = false;
128
+ break;
129
+ }
130
+ }
131
+ if (matches && (score > bestScore || score === bestScore && isLaterVersion(route, bestMatch?.route))) {
132
+ bestScore = score;
133
+ bestMatch = { route, params };
134
+ }
135
+ }
136
+ return bestMatch;
137
+ }
138
+ function isLaterVersion(a, b) {
139
+ if (!b) return true;
140
+ return a.version > b.version;
141
+ }
142
+ function suggestRoutes(manifest, userSegments) {
143
+ if (!userSegments.length) return [];
144
+ return manifest.routes.filter((route) => {
145
+ if (route.segments.length < userSegments.length) return false;
146
+ const firstSeg = route.segments[0];
147
+ return !firstSeg.isParam && firstSeg.value === userSegments[0];
148
+ });
149
+ }
150
+
151
+ export {
152
+ checkPermission,
153
+ explainPermission,
154
+ suggestedAllowRule,
155
+ resourceFromSegments,
156
+ resourceFromTool,
157
+ resourceFromRawPath,
158
+ matchRoute,
159
+ suggestRoutes
160
+ };
@@ -1,6 +1,18 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ AuthStrategy
4
+ } from "./chunk-YDXTIN53.js";
2
5
 
3
- // src/request.ts
6
+ // ../../interfaces/api/src/request.ts
7
+ var HttpStatusError = class extends Error {
8
+ constructor(status, statusText, body) {
9
+ super(body || `${status} ${statusText}`);
10
+ this.status = status;
11
+ this.statusText = statusText;
12
+ }
13
+ status;
14
+ statusText;
15
+ };
4
16
  async function executeToString(manifest, match, options) {
5
17
  const baseUrl = manifest.config.url;
6
18
  if (!baseUrl) {
@@ -16,25 +28,18 @@ async function executeToString(manifest, match, options) {
16
28
  }
17
29
  const headers = { ...manifest.config.headers, ...options.headers };
18
30
  const authConfig = manifest.config.auth;
19
- const token = options.token || (authConfig?.env ? process.env[authConfig.env] : void 0);
31
+ const token = authConfig?.env ? process.env[authConfig.env] : void 0;
20
32
  if (authConfig?.env && !token && !options.dryRun) {
21
- throw new Error(`Missing ${authConfig.env}. Set it in .env or environment, or use --token.`);
33
+ throw new Error(`Missing ${authConfig.env}. Set it in .env or export it in your shell.`);
22
34
  }
23
35
  if (token) {
24
- const authType = authConfig?.type || "bearer";
25
- if (authType === "bearer") {
26
- headers["Authorization"] = `Bearer ${token}`;
27
- } else if (authType === "api-key") {
28
- headers[authConfig?.header || "X-API-Key"] = token;
29
- } else if (authType === "basic") {
30
- headers["Authorization"] = `Basic ${token}`;
31
- }
36
+ AuthStrategy.for(authConfig).apply(headers, token);
32
37
  }
33
38
  if (options.body && !headers["Content-Type"]) {
34
39
  headers["Content-Type"] = "application/json";
35
40
  }
36
41
  const method = match.route.method.toUpperCase();
37
- if (options.dryRun || options.verbose) {
42
+ if (options.dryRun || options.debug) {
38
43
  process.stderr.write(`${method} ${url.toString()}
39
44
  `);
40
45
  for (const [k, v] of Object.entries(headers)) {
@@ -54,7 +59,7 @@ async function executeToString(manifest, match, options) {
54
59
  headers,
55
60
  body: options.body || void 0
56
61
  });
57
- if (options.verbose) {
62
+ if (options.debug) {
58
63
  process.stderr.write(`${response.status} ${response.statusText}
59
64
  `);
60
65
  response.headers.forEach((v, k) => process.stderr.write(` ${k}: ${v}
@@ -73,7 +78,7 @@ async function executeToString(manifest, match, options) {
73
78
  } else {
74
79
  result = text;
75
80
  }
76
- if (!response.ok) throw new Error(result);
81
+ if (!response.ok) throw new HttpStatusError(response.status, response.statusText, result);
77
82
  return result;
78
83
  }
79
84
  async function execute(manifest, match, options) {
@@ -81,6 +86,14 @@ async function execute(manifest, match, options) {
81
86
  const result = await executeToString(manifest, match, options);
82
87
  if (result) process.stdout.write(result + "\n");
83
88
  } catch (err) {
89
+ if (err instanceof HttpStatusError) {
90
+ process.stderr.write(`${err.status} ${err.statusText}
91
+ `);
92
+ if (err.message) process.stderr.write(err.message + "\n");
93
+ if (err.status >= 400 && err.status < 500) process.exit(10);
94
+ if (err.status >= 500) process.exit(11);
95
+ process.exit(12);
96
+ }
84
97
  process.stderr.write(err.message + "\n");
85
98
  process.exit(1);
86
99
  }