meksus 0.3.0 → 0.3.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "meksus",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "M.E.K.S.U.S. CLI: see what your AI coding agents (Claude Code) and builds are doing, live, and get Discord alerts when they fail or need you. Status only: code, prompts and output never leave your machine.",
5
5
  "keywords": [
6
6
  "meksus",
package/src/auth.mjs CHANGED
@@ -4,8 +4,8 @@
4
4
  // typed, pasted or shown:
5
5
  // 1. ask the ingest Worker where Supabase lives (/v1/cli/config: public values only)
6
6
  // 2. listen on http://127.0.0.1:<random port> for one callback
7
- // 3. open <supabase>/auth/v1/authorize?provider=github&code_challenge=… The browser comes back to
8
- // https://<app>/cli-callback, a static page that forwards ?code= to the local listener. The code is
7
+ // 3. open https://<app>/cli-callback?challenge=…, which forwards to <supabase>/auth/v1/authorize (GitHub,
8
+ // PKCE). The browser comes back to the same static page, which forwards ?code= to the local listener. The code is
9
9
  // useless without this process's verifier, which never leaves it.
10
10
  // 4. exchange code + verifier for a session; keep it in config.json (0600)
11
11
  // Access tokens last an hour; getAccessToken() refreshes them (rotating refresh token) when needed.
@@ -55,10 +55,9 @@ export async function login({ noBrowser = false } = {}) {
55
55
  await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
56
56
  const port = server.address().port;
57
57
 
58
- const redirect = `${cfg.app_url.replace(/\/$/, "")}/cli-callback?port=${port}&state=${state}`;
59
- const authorize = `${cfg.supabase_url.replace(/\/$/, "")}/auth/v1/authorize?${new URLSearchParams({
60
- provider: "github", redirect_to: redirect, code_challenge: challenge, code_challenge_method: "s256",
61
- })}`;
58
+ // The browser starts at the app's own /cli-callback page, which forwards to GitHub sign-in (through
59
+ // Supabase Auth) and later hands the code back here. Only public values travel: port, state, challenge.
60
+ const authorize = `${cfg.app_url.replace(/\/$/, "")}/cli-callback?${new URLSearchParams({ port: String(port), state, challenge })}`;
62
61
 
63
62
  console.log("\n Sign in to M.E.K.S.U.S. with GitHub\n");
64
63
  console.log(` Open: ${authorize}\n`);
package/src/hooks.mjs CHANGED
@@ -132,9 +132,29 @@ function settingsPath(cwd) {
132
132
  return join(d, ".claude", "settings.local.json");
133
133
  }
134
134
 
135
+ // Reads the settings file, or explains (and changes nothing) when it isn't valid JSON — often two
136
+ // { … } blocks pasted one after the other, which Claude Code can't read either.
137
+ function readSettings(file) {
138
+ if (!existsSync(file)) return {};
139
+ const text = readFileSync(file, "utf8");
140
+ try {
141
+ const value = JSON.parse(text);
142
+ if (value && typeof value === "object" && !Array.isArray(value)) return value;
143
+ } catch (error) {
144
+ const at = /line (\d+)/.exec(error.message)?.[1];
145
+ console.error(`✖ ${file} isn't valid JSON${at ? ` (line ${at})` : ""}, so nothing was changed.`);
146
+ if (/after JSON/.test(error.message)) console.error(" It holds more than one { … } block. Merge them into one object, then run this again.");
147
+ console.error(" Claude Code can't read this file either until it's fixed.");
148
+ return null;
149
+ }
150
+ console.error(`✖ ${file} should hold one JSON object, so nothing was changed.`);
151
+ return null;
152
+ }
153
+
135
154
  export function install(cwd = process.cwd()) {
136
155
  const file = settingsPath(cwd);
137
- const settings = existsSync(file) ? JSON.parse(readFileSync(file, "utf8")) : {};
156
+ const settings = readSettings(file);
157
+ if (!settings) return 1;
138
158
  settings.hooks ??= {};
139
159
  const command = `node "${CLI}" hook`;
140
160
  for (const event of EVENTS) {
@@ -155,7 +175,8 @@ export function install(cwd = process.cwd()) {
155
175
  export function uninstall(cwd = process.cwd()) {
156
176
  const file = settingsPath(cwd);
157
177
  if (!existsSync(file)) { console.log("No hooks file here."); return 0; }
158
- const settings = JSON.parse(readFileSync(file, "utf8"));
178
+ const settings = readSettings(file);
179
+ if (!settings) return 1;
159
180
  for (const event of Object.keys(settings.hooks ?? {})) {
160
181
  settings.hooks[event] = settings.hooks[event]
161
182
  .map((g) => ({ ...g, hooks: (g.hooks ?? []).filter((h) => !String(h.command).includes(MARK)) }))