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.
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/settings.ts
4
+ import { resolve } from "path";
5
+ import { existsSync, readFileSync } from "fs";
6
+ import { homedir } from "os";
7
+ import { parse as parseYaml } from "yaml";
8
+ function findProjectGodmode(start = process.cwd()) {
9
+ let dir = resolve(start);
10
+ while (true) {
11
+ const candidate = resolve(dir, ".godmode");
12
+ if (existsSync(candidate)) return candidate;
13
+ const parent = resolve(dir, "..");
14
+ if (parent === dir) return null;
15
+ dir = parent;
16
+ }
17
+ }
18
+ function readSettingsFile(path) {
19
+ if (!existsSync(path)) return { settings: {} };
20
+ try {
21
+ const raw = parseYaml(readFileSync(path, "utf-8"));
22
+ return { settings: raw ?? {} };
23
+ } catch (e) {
24
+ const msg = e.message || String(e);
25
+ return { settings: {}, error: { path, message: msg } };
26
+ }
27
+ }
28
+ function mergePermissions(base = {}, overlay = {}) {
29
+ const allow = [...base.allow ?? [], ...overlay.allow ?? []];
30
+ const deny = [...base.deny ?? [], ...overlay.deny ?? []];
31
+ if (!allow.length && !deny.length) return void 0;
32
+ const merged = {};
33
+ if (allow.length) merged.allow = allow;
34
+ if (deny.length) merged.deny = deny;
35
+ return merged;
36
+ }
37
+ function mergeExtension(base = {}, overlay = {}) {
38
+ const merged = { ...base, ...overlay };
39
+ const permissions = mergePermissions(base.permissions, overlay.permissions);
40
+ if (permissions) merged.permissions = permissions;
41
+ return merged;
42
+ }
43
+ function mergeSettings(base, overlay) {
44
+ const extensions = {};
45
+ const slugs = /* @__PURE__ */ new Set([
46
+ ...Object.keys(base.extensions ?? {}),
47
+ ...Object.keys(overlay.extensions ?? {})
48
+ ]);
49
+ for (const slug of slugs) {
50
+ extensions[slug] = mergeExtension(
51
+ base.extensions?.[slug],
52
+ overlay.extensions?.[slug]
53
+ );
54
+ }
55
+ return { extensions };
56
+ }
57
+ var cached = null;
58
+ function loadSettingsDetailed() {
59
+ if (cached) return cached;
60
+ const globalPath = resolve(homedir(), ".godmode", "settings.yaml");
61
+ const projectRoot = findProjectGodmode();
62
+ const projectPath = projectRoot ? resolve(projectRoot, "settings.yaml") : null;
63
+ const globalResult = readSettingsFile(globalPath);
64
+ const projectResult = projectPath ? readSettingsFile(projectPath) : { settings: {} };
65
+ const sources = [];
66
+ if (existsSync(globalPath)) sources.push({ scope: "global", path: globalPath, settings: globalResult.settings });
67
+ if (projectPath && existsSync(projectPath)) sources.push({ scope: "project", path: projectPath, settings: projectResult.settings });
68
+ cached = {
69
+ settings: mergeSettings(globalResult.settings, projectResult.settings),
70
+ errors: [globalResult.error, projectResult.error].filter(Boolean),
71
+ sources
72
+ };
73
+ return cached;
74
+ }
75
+ function loadSettings() {
76
+ return loadSettingsDetailed().settings;
77
+ }
78
+ function settingsErrorMessage(error) {
79
+ return `cannot parse ${error.path}: ${error.message} - refusing to run with an unreadable policy.`;
80
+ }
81
+ function warnSettingsErrors() {
82
+ for (const error of loadSettingsDetailed().errors) {
83
+ process.stderr.write(`Warning: ${settingsErrorMessage(error)}
84
+ `);
85
+ }
86
+ }
87
+ function resetSettingsCache() {
88
+ cached = null;
89
+ }
90
+ function extensionSettings(slug) {
91
+ const s = loadSettings();
92
+ return s.extensions?.[slug] ?? {};
93
+ }
94
+
95
+ export {
96
+ loadSettingsDetailed,
97
+ loadSettings,
98
+ settingsErrorMessage,
99
+ warnSettingsErrors,
100
+ resetSettingsCache,
101
+ extensionSettings
102
+ };
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/exit-codes.ts
4
+ var EXIT_CODES = {
5
+ success: 0,
6
+ usage: 2,
7
+ notFound: 3,
8
+ permissionDenied: 4,
9
+ upstream4xx: 10,
10
+ upstream5xx: 11,
11
+ upstream: 12
12
+ };
13
+
14
+ export {
15
+ EXIT_CODES
16
+ };
@@ -0,0 +1,212 @@
1
+ #!/usr/bin/env node
2
+
3
+ // ../cli-tools/src/auth.ts
4
+ var AuthStrategy = class {
5
+ constructor(config = {}) {
6
+ this.config = config;
7
+ }
8
+ config;
9
+ static for(config) {
10
+ const type = config?.type ?? "bearer";
11
+ switch (type) {
12
+ case "bearer":
13
+ return new BearerAuth(config ?? {});
14
+ case "api-key":
15
+ return new ApiKeyAuth(config ?? {});
16
+ case "basic":
17
+ return new BasicAuth(config ?? {});
18
+ }
19
+ }
20
+ };
21
+ var BearerAuth = class extends AuthStrategy {
22
+ label = "bearer";
23
+ apply(headers, token) {
24
+ headers["Authorization"] = `Bearer ${token}`;
25
+ }
26
+ missingMessage() {
27
+ return "missing bearer token";
28
+ }
29
+ };
30
+ var ApiKeyAuth = class extends AuthStrategy {
31
+ label = "api-key";
32
+ apply(headers, token) {
33
+ headers[this.config.header || "X-API-Key"] = token;
34
+ }
35
+ missingMessage() {
36
+ return "missing api-key";
37
+ }
38
+ };
39
+ var BasicAuth = class extends AuthStrategy {
40
+ label = "basic";
41
+ apply(headers, token) {
42
+ headers["Authorization"] = `Basic ${token}`;
43
+ }
44
+ missingMessage() {
45
+ return "missing basic auth credentials";
46
+ }
47
+ };
48
+
49
+ // ../cli-tools/src/index.ts
50
+ var USE_COLOR = process.stdout.isTTY;
51
+ var DIM = USE_COLOR ? "\x1B[2m" : "";
52
+ var ITALIC = USE_COLOR ? "\x1B[3m" : "";
53
+ var RESET = USE_COLOR ? "\x1B[0m" : "";
54
+ var RED = USE_COLOR ? "\x1B[31m" : "";
55
+ function visibleLength(s) {
56
+ return s.replace(/\x1b\[[0-9;]*m/g, "").length;
57
+ }
58
+ function wrapText(text, width) {
59
+ if (text.length <= width) return [text];
60
+ const out = [];
61
+ let line = "";
62
+ for (const word of text.split(/\s+/)) {
63
+ if (!line) {
64
+ line = word;
65
+ continue;
66
+ }
67
+ if (line.length + 1 + word.length <= width) {
68
+ line += " " + word;
69
+ } else {
70
+ out.push(line);
71
+ line = word;
72
+ }
73
+ }
74
+ if (line) out.push(line);
75
+ return out;
76
+ }
77
+ function renderSections(sections, maxLineWidth = 80) {
78
+ const INDENT = " ";
79
+ const GAP = " ";
80
+ const flattened = sections.map((s) => {
81
+ if (!s.rows.length) return { title: s.title, rows: [] };
82
+ const cols = Math.max(...s.rows.map((r) => r.length));
83
+ const widths = Array(cols).fill(0);
84
+ for (const row of s.rows) {
85
+ for (let i = 0; i < cols; i++) {
86
+ widths[i] = Math.max(widths[i], visibleLength(row[i] ?? ""));
87
+ }
88
+ }
89
+ const rows = s.rows.map((row) => {
90
+ const left = row.slice(0, cols - 1).map((cell, i) => (cell ?? "") + " ".repeat(widths[i] - visibleLength(cell ?? ""))).join(GAP).replace(/\s+$/, "");
91
+ const desc = row[cols - 1] ?? "";
92
+ return [left, desc];
93
+ });
94
+ return { title: s.title, rows };
95
+ });
96
+ const allRows = flattened.flatMap((s) => s.rows);
97
+ if (!allRows.length) return;
98
+ const rowsWithDesc = allRows.filter((r) => r[1].length > 0);
99
+ const widthCandidates = (rowsWithDesc.length ? rowsWithDesc : allRows).map((r) => visibleLength(r[0]));
100
+ const globalLeft = Math.max(...widthCandidates);
101
+ const descBudget = Math.max(20, maxLineWidth - INDENT.length - globalLeft - GAP.length);
102
+ for (const s of flattened) {
103
+ if (!s.rows.length) continue;
104
+ if (s.title) {
105
+ console.log("");
106
+ console.log(s.title);
107
+ }
108
+ for (const [left, desc] of s.rows) {
109
+ const pad = " ".repeat(Math.max(0, globalLeft - visibleLength(left)));
110
+ const plain = desc.replace(/\x1b\[[0-9;]*m/g, "");
111
+ if (!plain) {
112
+ console.log(`${INDENT}${left}${pad}`.trimEnd());
113
+ continue;
114
+ }
115
+ if (plain.length <= descBudget) {
116
+ console.log(`${INDENT}${left}${pad}${GAP}${desc}`);
117
+ continue;
118
+ }
119
+ const italic = /^\x1b\[3m/.test(desc);
120
+ const wrap = (s2) => italic ? `${ITALIC}${s2}${RESET}` : s2;
121
+ const lines = wrapText(plain, Math.max(10, descBudget));
122
+ console.log(`${INDENT}${left}${pad}${GAP}${wrap(lines[0])}`);
123
+ for (let i = 1; i < lines.length; i++) {
124
+ console.log(`${INDENT}${" ".repeat(Math.max(0, globalLeft))}${GAP}${wrap(lines[i])}`);
125
+ }
126
+ }
127
+ }
128
+ }
129
+ function authMissingLabel(type) {
130
+ return AuthStrategy.for({ type }).missingMessage();
131
+ }
132
+ var HelpPage = class {
133
+ title() {
134
+ return null;
135
+ }
136
+ tagline() {
137
+ return null;
138
+ }
139
+ authNote() {
140
+ return null;
141
+ }
142
+ usage() {
143
+ return [];
144
+ }
145
+ sections() {
146
+ return [];
147
+ }
148
+ footer() {
149
+ return null;
150
+ }
151
+ toJSON() {
152
+ return {
153
+ title: this.title(),
154
+ tagline: this.tagline(),
155
+ authNote: this.authNote(),
156
+ usage: this.usage(),
157
+ sections: this.sections(),
158
+ footer: this.footer()
159
+ };
160
+ }
161
+ render() {
162
+ const d = this.toJSON();
163
+ let wrote = false;
164
+ if (d.title) {
165
+ console.log(d.title);
166
+ wrote = true;
167
+ }
168
+ if (d.tagline) {
169
+ if (wrote) console.log("");
170
+ console.log(d.tagline);
171
+ wrote = true;
172
+ }
173
+ if (d.authNote && !d.authNote.present) {
174
+ const arrow = USE_COLOR ? `${RED}-->${RESET}` : "-->";
175
+ const label = authMissingLabel(d.authNote.authType);
176
+ const body = USE_COLOR ? `${RED}${label}${RESET}` : label;
177
+ if (wrote) console.log("");
178
+ console.log(`${arrow} ${d.authNote.env}: ${body}`);
179
+ wrote = true;
180
+ }
181
+ if (d.usage.length) {
182
+ if (wrote) console.log("");
183
+ for (let i = 0; i < d.usage.length; i++) {
184
+ const prefix = i === 0 ? "Usage:" : " or:";
185
+ console.log(`${prefix} ${d.usage[i]}`);
186
+ }
187
+ wrote = true;
188
+ }
189
+ renderSections(d.sections);
190
+ if (d.footer) {
191
+ if (d.footer.extras?.length) {
192
+ console.log("");
193
+ for (const line of d.footer.extras) console.log(line);
194
+ }
195
+ if (d.footer.reportBugs || d.footer.homepage) {
196
+ console.log("");
197
+ if (d.footer.reportBugs) console.log(`Report bugs to <${d.footer.reportBugs}>.`);
198
+ if (d.footer.homepage) console.log(`Godmode home page: <${d.footer.homepage}>.`);
199
+ }
200
+ }
201
+ }
202
+ };
203
+
204
+ export {
205
+ AuthStrategy,
206
+ USE_COLOR,
207
+ DIM,
208
+ ITALIC,
209
+ RESET,
210
+ RED,
211
+ HelpPage
212
+ };