@dunnewold-labs/mr-manager 0.1.0 → 0.2.0

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.
Files changed (2) hide show
  1. package/dist/index.mjs +58 -13
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -89,23 +89,62 @@ function openBrowser(url) {
89
89
  const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
90
90
  spawn(cmd, [url], { detached: true, stdio: "ignore" }).unref();
91
91
  }
92
- var loginCommand = new Command3("login").description("Authenticate the CLI via browser (Google OAuth)").option("--url <url>", "API URL", "https://mr-manager-gold.vercel.app").action(async (opts) => {
93
- const config = loadConfig();
94
- const apiUrl = opts.url ?? config.apiUrl ?? "https://mr-manager-gold.vercel.app";
92
+ function isHeadless() {
93
+ if (process.env.SSH_CLIENT || process.env.SSH_TTY || process.env.SSH_CONNECTION) {
94
+ return true;
95
+ }
96
+ if (process.platform === "linux" && !process.env.DISPLAY && !process.env.WAYLAND_DISPLAY) {
97
+ return true;
98
+ }
99
+ return false;
100
+ }
101
+ async function loginWithDeviceCode(apiUrl) {
102
+ const res = await fetch(`${apiUrl}/api/auth/device-code`, { method: "POST" });
103
+ if (!res.ok) {
104
+ throw new Error(`Failed to create device code: ${res.status} ${res.statusText}`);
105
+ }
106
+ const { code } = await res.json();
107
+ const loginUrl = `${apiUrl}/login?deviceCode=${code}`;
108
+ console.log(`
109
+ Remote login \u2014 visit this URL in any browser:
110
+ `);
111
+ console.log(` ${loginUrl}
112
+ `);
113
+ console.log(`Waiting for authentication\u2026`);
114
+ const timeout = 10 * 60 * 1e3;
115
+ const interval = 3e3;
116
+ const start = Date.now();
117
+ while (Date.now() - start < timeout) {
118
+ await new Promise((r) => setTimeout(r, interval));
119
+ const pollRes = await fetch(`${apiUrl}/api/auth/device-code/poll?code=${code}`);
120
+ if (pollRes.status === 410) {
121
+ throw new Error("Device code expired. Please try again.");
122
+ }
123
+ if (!pollRes.ok) {
124
+ continue;
125
+ }
126
+ const data = await pollRes.json();
127
+ if (data.status === "complete" && data.key) {
128
+ return data.key;
129
+ }
130
+ }
131
+ throw new Error("Login timed out after 10 minutes");
132
+ }
133
+ async function loginWithLocalServer(apiUrl) {
95
134
  const port = getRandomPort();
96
- const key = await new Promise((resolve7, reject) => {
135
+ return new Promise((resolve7, reject) => {
97
136
  const server = createServer((req, res) => {
98
137
  const url = new URL(req.url ?? "/", `http://localhost:${port}`);
99
- const key2 = url.searchParams.get("key");
138
+ const key = url.searchParams.get("key");
100
139
  res.writeHead(200, { "Content-Type": "text/html" });
101
140
  res.end(`
102
- <html><body style="font-family:sans-serif;text-align:center;padding:60px">
103
- <h2>${key2 ? "\u2713 Authenticated!" : "Something went wrong."}</h2>
104
- <p>${key2 ? "You can close this tab and return to the terminal." : "No key received. Please try again."}</p>
105
- </body></html>
106
- `);
141
+ <html><body style="font-family:sans-serif;text-align:center;padding:60px">
142
+ <h2>${key ? "\u2713 Authenticated!" : "Something went wrong."}</h2>
143
+ <p>${key ? "You can close this tab and return to the terminal." : "No key received. Please try again."}</p>
144
+ </body></html>
145
+ `);
107
146
  server.close();
108
- if (key2) resolve7(key2);
147
+ if (key) resolve7(key);
109
148
  else reject(new Error("No key received from server"));
110
149
  });
111
150
  server.listen(port, () => {
@@ -123,6 +162,12 @@ Opening browser for authentication\u2026`);
123
162
  reject(new Error("Login timed out after 5 minutes"));
124
163
  }, 5 * 60 * 1e3);
125
164
  });
165
+ }
166
+ var loginCommand = new Command3("login").description("Authenticate the CLI via browser (Google OAuth)").option("--url <url>", "API URL", "https://mr-manager-gold.vercel.app").option("--headless", "Use device-code flow for remote/headless environments").action(async (opts) => {
167
+ const config = loadConfig();
168
+ const apiUrl = opts.url ?? config.apiUrl ?? "https://mr-manager-gold.vercel.app";
169
+ const useDeviceCode = opts.headless || isHeadless();
170
+ const key = useDeviceCode ? await loginWithDeviceCode(apiUrl) : await loginWithLocalServer(apiUrl);
126
171
  config.apiKey = key;
127
172
  config.apiUrl = apiUrl;
128
173
  saveConfig(config);
@@ -143,7 +188,7 @@ try {
143
188
  const pkg = JSON.parse(readFileSync2(join2(__dirname, "..", "package.json"), "utf-8"));
144
189
  cliVersion = pkg.version;
145
190
  } catch {
146
- cliVersion = "0.1.0";
191
+ cliVersion = "0.2.0";
147
192
  }
148
193
  var ApiError = class extends Error {
149
194
  constructor(status, statusText, body) {
@@ -5510,7 +5555,7 @@ if (isFirstRun && !shouldBypass) {
5510
5555
  process.exit(0);
5511
5556
  }
5512
5557
  var program = new Command29();
5513
- program.name("mr").description("Mr. Manager - Task and project management CLI").version("0.1.0");
5558
+ program.name("mr").description("Mr. Manager - Task and project management CLI").version("0.2.0");
5514
5559
  program.addCommand(initCommand);
5515
5560
  program.addCommand(authCommand);
5516
5561
  program.addCommand(loginCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dunnewold-labs/mr-manager",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Mr. Manager - Task and project management CLI",
5
5
  "bin": {
6
6
  "mr": "./dist/index.mjs"