@getrouter/getrouter-cli 0.1.7 → 0.1.9

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/dist/bin.mjs CHANGED
@@ -8,7 +8,7 @@ import { randomInt } from "node:crypto";
8
8
  import prompts from "prompts";
9
9
 
10
10
  //#region package.json
11
- var version = "0.1.7";
11
+ var version = "0.1.9";
12
12
 
13
13
  //#endregion
14
14
  //#region src/generated/router/dashboard/v1/index.ts
@@ -369,12 +369,15 @@ const shouldRetryResponse = (error) => {
369
369
  if (typeof error === "object" && error !== null && "status" in error && typeof error.status === "number") return isServerError(error.status);
370
370
  return error instanceof TypeError;
371
371
  };
372
- const requestJson = async ({ path: path$1, method, body, fetchImpl, maxRetries = 3, _retrySleep }) => {
372
+ const requestJson = async ({ path: path$1, method, body, fetchImpl, maxRetries = 3, includeAuth = true, _retrySleep }) => {
373
373
  return withRetry(async () => {
374
- const auth = readAuth();
374
+ const auth = includeAuth ? readAuth() : {
375
+ accessToken: void 0,
376
+ refreshToken: void 0
377
+ };
375
378
  const url = buildApiUrl(path$1);
376
- let res = await doFetch(url, method, buildHeaders(auth.accessToken), body, fetchImpl);
377
- if (res.status === 401 && auth.refreshToken) {
379
+ let res = await doFetch(url, method, includeAuth ? buildHeaders(auth.accessToken) : buildHeaders(), body, fetchImpl);
380
+ if (includeAuth && res.status === 401 && auth.refreshToken) {
378
381
  const refreshed = await refreshAccessToken({ fetchImpl });
379
382
  if (refreshed?.accessToken) res = await doFetch(url, method, buildHeaders(refreshed.accessToken), body, fetchImpl);
380
383
  }
@@ -389,7 +392,7 @@ const requestJson = async ({ path: path$1, method, body, fetchImpl, maxRetries =
389
392
 
390
393
  //#endregion
391
394
  //#region src/core/api/client.ts
392
- const createApiClients = ({ fetchImpl, clients }) => {
395
+ const createApiClients = ({ fetchImpl, clients, includeAuth = true }) => {
393
396
  const factories = clients ?? {
394
397
  createConsumerServiceClient,
395
398
  createAuthServiceClient,
@@ -402,7 +405,8 @@ const createApiClients = ({ fetchImpl, clients }) => {
402
405
  path: path$1,
403
406
  method,
404
407
  body: body ? JSON.parse(body) : void 0,
405
- fetchImpl
408
+ fetchImpl,
409
+ includeAuth
406
410
  });
407
411
  };
408
412
  return {
@@ -446,31 +450,38 @@ const generateAuthCode = () => {
446
450
  return out;
447
451
  };
448
452
  const buildLoginUrl = (authCode) => `https://getrouter.dev/auth/${authCode}`;
453
+ const spawnBrowser = (command, args) => {
454
+ try {
455
+ const child = spawn(command, args, {
456
+ stdio: "ignore",
457
+ detached: true
458
+ });
459
+ child.on("error", (err) => {
460
+ const code = typeof err === "object" && err !== null && "code" in err ? err.code : void 0;
461
+ const reason = code === "ENOENT" ? ` (${command} not found)` : code ? ` (${code})` : "";
462
+ console.log(`⚠️ Unable to open browser${reason}. Please open the URL manually.`);
463
+ });
464
+ child.unref();
465
+ } catch {
466
+ console.log("⚠️ Unable to open browser. Please open the URL manually.");
467
+ }
468
+ };
449
469
  const openLoginUrl = async (url) => {
450
470
  try {
451
471
  if (process.platform === "darwin") {
452
- spawn("open", [url], {
453
- stdio: "ignore",
454
- detached: true
455
- }).unref();
472
+ spawnBrowser("open", [url]);
456
473
  return;
457
474
  }
458
475
  if (process.platform === "win32") {
459
- spawn("cmd", [
476
+ spawnBrowser("cmd", [
460
477
  "/c",
461
478
  "start",
462
479
  "",
463
480
  url
464
- ], {
465
- stdio: "ignore",
466
- detached: true
467
- }).unref();
481
+ ]);
468
482
  return;
469
483
  }
470
- spawn("xdg-open", [url], {
471
- stdio: "ignore",
472
- detached: true
473
- }).unref();
484
+ spawnBrowser("xdg-open", [url]);
474
485
  } catch {}
475
486
  };
476
487
  const pollAuthorize = async ({ authorize, code, timeoutMs = 300 * 1e3, initialDelayMs = 1e3, maxDelayMs = 1e4, sleep = (ms) => new Promise((r) => setTimeout(r, ms)), now = () => Date.now(), onRetry }) => {
@@ -498,7 +509,7 @@ const pollAuthorize = async ({ authorize, code, timeoutMs = 300 * 1e3, initialDe
498
509
  //#region src/cmd/auth.ts
499
510
  const registerAuthCommands = (program) => {
500
511
  program.command("login").description("Login with device flow").action(async () => {
501
- const { authService } = createApiClients({});
512
+ const { authService } = createApiClients({ includeAuth: false });
502
513
  const authCode = generateAuthCode();
503
514
  const url = buildLoginUrl(authCode);
504
515
  console.log("🔐 To authenticate, visit:");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getrouter/getrouter-cli",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "type": "module",
5
5
  "description": "CLI for getrouter.dev",
6
6
  "bin": {
package/src/cmd/auth.ts CHANGED
@@ -14,7 +14,7 @@ export const registerAuthCommands = (program: Command) => {
14
14
  .command("login")
15
15
  .description("Login with device flow")
16
16
  .action(async () => {
17
- const { authService } = createApiClients({});
17
+ const { authService } = createApiClients({ includeAuth: false });
18
18
  const authCode = generateAuthCode();
19
19
  const url = buildLoginUrl(authCode);
20
20
  console.log("🔐 To authenticate, visit:");
@@ -46,9 +46,11 @@ export type ApiClients = {
46
46
  export const createApiClients = ({
47
47
  fetchImpl,
48
48
  clients,
49
+ includeAuth = true,
49
50
  }: {
50
51
  fetchImpl?: typeof fetch;
51
52
  clients?: ClientFactories;
53
+ includeAuth?: boolean;
52
54
  }): ApiClients => {
53
55
  const factories =
54
56
  clients ??
@@ -66,6 +68,7 @@ export const createApiClients = ({
66
68
  method,
67
69
  body: body ? JSON.parse(body) : undefined,
68
70
  fetchImpl,
71
+ includeAuth,
69
72
  });
70
73
  };
71
74
 
@@ -33,29 +33,44 @@ export const generateAuthCode = () => {
33
33
  export const buildLoginUrl = (authCode: string) =>
34
34
  `https://getrouter.dev/auth/${authCode}`;
35
35
 
36
+ const spawnBrowser = (command: string, args: string[]) => {
37
+ try {
38
+ const child = spawn(command, args, {
39
+ stdio: "ignore",
40
+ detached: true,
41
+ });
42
+ child.on("error", (err) => {
43
+ const code =
44
+ typeof err === "object" && err !== null && "code" in err
45
+ ? (err as { code?: string }).code
46
+ : undefined;
47
+ const reason =
48
+ code === "ENOENT"
49
+ ? ` (${command} not found)`
50
+ : code
51
+ ? ` (${code})`
52
+ : "";
53
+ console.log(
54
+ `⚠️ Unable to open browser${reason}. Please open the URL manually.`,
55
+ );
56
+ });
57
+ child.unref();
58
+ } catch {
59
+ console.log("⚠️ Unable to open browser. Please open the URL manually.");
60
+ }
61
+ };
62
+
36
63
  export const openLoginUrl = async (url: string) => {
37
64
  try {
38
65
  if (process.platform === "darwin") {
39
- const child = spawn("open", [url], {
40
- stdio: "ignore",
41
- detached: true,
42
- });
43
- child.unref();
66
+ spawnBrowser("open", [url]);
44
67
  return;
45
68
  }
46
69
  if (process.platform === "win32") {
47
- const child = spawn("cmd", ["/c", "start", "", url], {
48
- stdio: "ignore",
49
- detached: true,
50
- });
51
- child.unref();
70
+ spawnBrowser("cmd", ["/c", "start", "", url]);
52
71
  return;
53
72
  }
54
- const child = spawn("xdg-open", [url], {
55
- stdio: "ignore",
56
- detached: true,
57
- });
58
- child.unref();
73
+ spawnBrowser("xdg-open", [url]);
59
74
  } catch {
60
75
  // best effort
61
76
  }
@@ -10,6 +10,7 @@ type RequestInput = {
10
10
  body?: unknown;
11
11
  fetchImpl?: typeof fetch;
12
12
  maxRetries?: number;
13
+ includeAuth?: boolean;
13
14
  /** For testing: override the sleep function used for retry delays */
14
15
  _retrySleep?: (ms: number) => Promise<void>;
15
16
  };
@@ -63,18 +64,23 @@ export const requestJson = async <T = unknown>({
63
64
  body,
64
65
  fetchImpl,
65
66
  maxRetries = 3,
67
+ includeAuth = true,
66
68
  _retrySleep,
67
69
  }: RequestInput): Promise<T> => {
68
70
  return withRetry(
69
71
  async () => {
70
- const auth = readAuth();
72
+ const auth = includeAuth
73
+ ? readAuth()
74
+ : { accessToken: undefined, refreshToken: undefined };
71
75
  const url = buildApiUrl(path);
72
- const headers = buildHeaders(auth.accessToken);
76
+ const headers = includeAuth
77
+ ? buildHeaders(auth.accessToken)
78
+ : buildHeaders();
73
79
 
74
80
  let res = await doFetch(url, method, headers, body, fetchImpl);
75
81
 
76
82
  // On 401, attempt token refresh and retry once
77
- if (res.status === 401 && auth.refreshToken) {
83
+ if (includeAuth && res.status === 401 && auth.refreshToken) {
78
84
  const refreshed = await refreshAccessToken({ fetchImpl });
79
85
  if (refreshed?.accessToken) {
80
86
  const newHeaders = buildHeaders(refreshed.accessToken);
@@ -51,6 +51,36 @@ describe("requestJson", () => {
51
51
  expect(headers.Cookie).toBe("access_token=t");
52
52
  });
53
53
 
54
+ it("skips auth headers when includeAuth is false", async () => {
55
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "getrouter-"));
56
+ process.env.GETROUTER_CONFIG_DIR = dir;
57
+ fs.writeFileSync(
58
+ path.join(dir, "auth.json"),
59
+ JSON.stringify({ accessToken: "t" }),
60
+ );
61
+
62
+ const fetchSpy = vi.fn(
63
+ async (_input: RequestInfo | URL, _init?: RequestInit) =>
64
+ ({
65
+ ok: true,
66
+ json: async () => ({ ok: true }),
67
+ }) as Response,
68
+ );
69
+
70
+ await requestJson({
71
+ path: "/v1/test",
72
+ method: "GET",
73
+ fetchImpl: fetchSpy as unknown as typeof fetch,
74
+ includeAuth: false,
75
+ });
76
+
77
+ const call = fetchSpy.mock.calls[0] as Parameters<typeof fetch> | undefined;
78
+ const init = call?.[1];
79
+ const headers = (init?.headers ?? {}) as Record<string, string>;
80
+ expect(headers.Authorization).toBeUndefined();
81
+ expect(headers.Cookie).toBeUndefined();
82
+ });
83
+
54
84
  it("uses GETROUTER_AUTH_COOKIE when set", async () => {
55
85
  process.env.GETROUTER_AUTH_COOKIE = "router_auth";
56
86
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), "getrouter-"));