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.
@@ -1,18 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  addApi
4
- } from "./chunk-2J2VDCQ4.js";
5
- import "./chunk-EXWYJU2I.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
11
  console.log(`Add an extension from a folder, built-in name, or manifest.
11
12
 
12
13
  Usage:
13
- godmode extension add <name> Built-in extension (e.g. stripe, github)
14
- godmode extension add <folder> Folder containing manifest.yaml
15
- godmode extension add <manifest> Direct path to manifest.yaml/json
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
16
17
 
17
18
  Manifest format (manifest.yaml):
18
19
  slug: stripe CLI name
@@ -33,13 +34,13 @@ Types:
33
34
  mcp MCP endpoint (requires url)
34
35
 
35
36
  Examples:
36
- $ godmode extension add stripe
37
- $ godmode extension add ./my-extension
38
- $ godmode extension add ./my-extension/manifest.yaml
39
- $ godmode extension 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`);
40
41
  process.exit(args[0] ? 0 : 1);
41
42
  }
42
- await addApi(args[0]);
43
+ await addApi(args[0], scope);
43
44
  }
44
45
  export {
45
46
  runAdd
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ AuthStrategy
4
+ } from "./chunk-YDXTIN53.js";
2
5
 
3
6
  // ../../interfaces/mcp/src/index.ts
4
7
  var requestId = 0;
@@ -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
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) {
@@ -21,20 +33,13 @@ async function executeToString(manifest, match, options) {
21
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
  }