@learning-with-court/cli 0.1.1 → 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.
@@ -1,6 +1,7 @@
1
1
  export interface CallbackResult {
2
2
  type: 'callback';
3
- code: string;
3
+ /** The Clerk-templated JWT minted by the cli-auth page. */
4
+ token: string;
4
5
  state: string;
5
6
  }
6
7
  export interface CancelResult {
@@ -15,7 +16,7 @@ export interface LocalServerHandle {
15
16
  }
16
17
  /**
17
18
  * Start a local HTTP server on an OS-assigned ephemeral port. Resolves
18
- * `done` when the browser hits `/callback?code=...&state=...` or
19
+ * `done` when the browser hits `/callback?token=...&state=...` or
19
20
  * `/cancel?state=...`. The caller is responsible for validating that the
20
21
  * returned `state` matches what they generated.
21
22
  *
@@ -10,7 +10,7 @@ const CANCEL_HTML = `<!doctype html>
10
10
  const NOT_FOUND_HTML = '<h1>Not found</h1>';
11
11
  /**
12
12
  * Start a local HTTP server on an OS-assigned ephemeral port. Resolves
13
- * `done` when the browser hits `/callback?code=...&state=...` or
13
+ * `done` when the browser hits `/callback?token=...&state=...` or
14
14
  * `/cancel?state=...`. The caller is responsible for validating that the
15
15
  * returned `state` matches what they generated.
16
16
  *
@@ -30,16 +30,16 @@ export function startLocalCallbackServer(opts = {}) {
30
30
  const host = req.headers.host ?? 'localhost';
31
31
  const reqUrl = new URL(req.url ?? '/', `http://${host}`);
32
32
  if (reqUrl.pathname === '/callback') {
33
- const code = reqUrl.searchParams.get('code');
33
+ const token = reqUrl.searchParams.get('token');
34
34
  const state = reqUrl.searchParams.get('state');
35
- if (!code || !state) {
35
+ if (!token || !state) {
36
36
  res.writeHead(400, { 'Content-Type': 'text/html' });
37
- res.end('<h1>Missing code or state</h1>');
37
+ res.end('<h1>Missing token or state</h1>');
38
38
  return;
39
39
  }
40
40
  res.writeHead(200, { 'Content-Type': 'text/html' });
41
41
  res.end(SUCCESS_HTML);
42
- resolveDone({ type: 'callback', code, state });
42
+ resolveDone({ type: 'callback', token, state });
43
43
  return;
44
44
  }
45
45
  if (reqUrl.pathname === '/cancel') {
@@ -1 +1 @@
1
- {"version":3,"file":"local-server.js","sourceRoot":"","sources":["../../src/auth/local-server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"local-server.js","sourceRoot":"","sources":["../../src/auth/local-server.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,16 @@
1
1
  import { type CachedToken } from './token-cache.js';
2
2
  /**
3
- * Drive the full browser-based login flow. Opens the user's browser to
4
- * `<landing>/cli-auth?...`, waits for them to complete the authorize
5
- * dance, exchanges the resulting code for a JWT, and returns it. Does
6
- * NOT cache the token caller is responsible for that.
3
+ * Drive the browser-based sign-in flow. Opens
4
+ * `<landing>/cli-auth?...&port=<loopback-port>[&refresh=1]`, waits for
5
+ * the user to complete the consent (or for the page to auto-mint when
6
+ * `refresh=true` and they have an active Clerk session), and returns
7
+ * the resulting Clerk-templated JWT. Caller is responsible for caching.
8
+ *
9
+ * `refresh=true` puts the cli-auth page into auto-mint mode: it skips
10
+ * the consent card render and mints the JWT immediately if the user is
11
+ * still signed into Clerk. If their Clerk session is gone, they fall
12
+ * back to the normal sign-in path. Use this for re-auth after expiry.
7
13
  */
8
- export declare function loginViaBrowser(): Promise<CachedToken>;
14
+ export declare function loginViaBrowser(opts?: {
15
+ refresh?: boolean;
16
+ }): Promise<CachedToken>;
@@ -1,6 +1,6 @@
1
1
  import { spawn } from 'node:child_process';
2
- import { cliAuthPageUrl, tokenExchangeUrl } from '../config.js';
3
- import { generatePkcePair, generateState } from './pkce.js';
2
+ import { randomBytes } from 'node:crypto';
3
+ import { cliAuthPageUrl } from '../config.js';
4
4
  import { startLocalCallbackServer } from './local-server.js';
5
5
  import { decodeJwtPayload } from './token-cache.js';
6
6
  function openBrowser(url) {
@@ -13,53 +13,64 @@ function openBrowser(url) {
13
13
  const child = spawn(cmd, args, { stdio: 'ignore', detached: true, shell: process.platform === 'win32' });
14
14
  child.unref();
15
15
  }
16
+ function generateState() {
17
+ return randomBytes(16).toString('hex');
18
+ }
16
19
  function buildAuthorizeUrl(params) {
17
20
  const url = new URL(cliAuthPageUrl());
18
21
  url.searchParams.set('state', params.state);
19
- url.searchParams.set('code_challenge', params.codeChallenge);
20
- url.searchParams.set('code_challenge_method', 'S256');
21
22
  url.searchParams.set('port', String(params.port));
23
+ if (params.refresh) {
24
+ url.searchParams.set('refresh', '1');
25
+ }
22
26
  return url.toString();
23
27
  }
24
- async function exchangeCodeForToken(params) {
25
- const response = await fetch(tokenExchangeUrl(), {
26
- method: 'POST',
27
- headers: { 'Content-Type': 'application/json' },
28
- body: JSON.stringify({ code: params.code, code_verifier: params.codeVerifier }),
29
- });
30
- if (!response.ok) {
31
- const text = await response.text().catch(() => '');
32
- throw new Error(`Token exchange failed (${response.status}): ${text}`);
28
+ function parseCachedToken(jwt) {
29
+ const claims = decodeJwtPayload(jwt);
30
+ if (!claims) {
31
+ throw new Error('Invalid JWT received from cli-auth callback (could not decode).');
32
+ }
33
+ const sub = typeof claims.sub === 'string' ? claims.sub : '';
34
+ const expFromJwt = typeof claims.exp === 'number' ? claims.exp * 1000 : null;
35
+ if (!expFromJwt) {
36
+ throw new Error('JWT is missing an exp claim.');
33
37
  }
34
- const data = (await response.json());
35
- const claims = decodeJwtPayload(data.access_token);
36
- const sub = typeof claims?.sub === 'string' ? claims.sub : '';
37
- const expFromJwt = typeof claims?.exp === 'number' ? claims.exp * 1000 : null;
38
- const expFromExpiresIn = Date.now() + data.expires_in * 1000;
38
+ const scope = typeof claims.scope === 'string' ? claims.scope : '';
39
39
  return {
40
- accessToken: data.access_token,
41
- tokenType: data.token_type ?? 'Bearer',
42
- expiresAt: expFromJwt ?? expFromExpiresIn,
43
- scope: data.scope ?? '',
40
+ accessToken: jwt,
41
+ tokenType: 'Bearer',
42
+ expiresAt: expFromJwt,
43
+ scope,
44
44
  sub,
45
45
  };
46
46
  }
47
47
  /**
48
- * Drive the full browser-based login flow. Opens the user's browser to
49
- * `<landing>/cli-auth?...`, waits for them to complete the authorize
50
- * dance, exchanges the resulting code for a JWT, and returns it. Does
51
- * NOT cache the token caller is responsible for that.
48
+ * Drive the browser-based sign-in flow. Opens
49
+ * `<landing>/cli-auth?...&port=<loopback-port>[&refresh=1]`, waits for
50
+ * the user to complete the consent (or for the page to auto-mint when
51
+ * `refresh=true` and they have an active Clerk session), and returns
52
+ * the resulting Clerk-templated JWT. Caller is responsible for caching.
53
+ *
54
+ * `refresh=true` puts the cli-auth page into auto-mint mode: it skips
55
+ * the consent card render and mints the JWT immediately if the user is
56
+ * still signed into Clerk. If their Clerk session is gone, they fall
57
+ * back to the normal sign-in path. Use this for re-auth after expiry.
52
58
  */
53
- export async function loginViaBrowser() {
59
+ export async function loginViaBrowser(opts = {}) {
60
+ const refresh = opts.refresh ?? false;
54
61
  const state = generateState();
55
- const { codeVerifier, codeChallenge } = generatePkcePair();
56
62
  const server = await startLocalCallbackServer();
57
63
  const authorizeUrl = buildAuthorizeUrl({
58
64
  state,
59
- codeChallenge,
60
65
  port: server.port,
66
+ refresh,
61
67
  });
62
- console.error('Opening browser to sign in...');
68
+ if (refresh) {
69
+ console.error('Refreshing your sign-in...');
70
+ }
71
+ else {
72
+ console.error('Opening browser to sign in...');
73
+ }
63
74
  console.error(`If your browser doesn't open, visit:\n ${authorizeUrl}`);
64
75
  openBrowser(authorizeUrl);
65
76
  let result;
@@ -76,9 +87,6 @@ export async function loginViaBrowser() {
76
87
  if (result.type === 'cancel') {
77
88
  throw new Error('Sign-in cancelled. Run `lwc auth login` to retry.');
78
89
  }
79
- return exchangeCodeForToken({
80
- code: result.code,
81
- codeVerifier,
82
- });
90
+ return parseCachedToken(result.token);
83
91
  }
84
92
  //# sourceMappingURL=login.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/auth/login.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/auth/login.ts"],"names":[],"mappings":"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"}
@@ -8,10 +8,12 @@ export async function resolveBearerToken() {
8
8
  if (cached && !isTokenExpired(cached)) {
9
9
  return cached.accessToken;
10
10
  }
11
- // No cached token, or it expired. Drive the browser flow. The new CLI
12
- // tokens are 90-day JWTs with no refresh token — re-auth is the
13
- // recovery path on expiry.
14
- const fresh = await loginViaBrowser();
11
+ // Cache is missing or expired. Drive the browser flow:
12
+ // - cached + expired refresh mode (cli-auth auto-mints if Clerk
13
+ // session is alive; user sees a brief browser flash)
14
+ // - no cached token → first-time consent flow
15
+ const refresh = cached !== null;
16
+ const fresh = await loginViaBrowser({ refresh });
15
17
  saveCachedToken(fresh);
16
18
  return fresh.accessToken;
17
19
  }
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-token.js","sourceRoot":"","sources":["../../src/auth/resolve-token.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,eAAe,EACf,cAAc,GACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;IACvC,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;IACjC,IAAI,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,OAAO,MAAM,CAAC,WAAW,CAAC;IAC5B,CAAC;IAED,sEAAsE;IACtE,gEAAgE;IAChE,2BAA2B;IAC3B,MAAM,KAAK,GAAG,MAAM,eAAe,EAAE,CAAC;IACtC,eAAe,CAAC,KAAK,CAAC,CAAC;IACvB,OAAO,KAAK,CAAC,WAAW,CAAC;AAC3B,CAAC"}
1
+ {"version":3,"file":"resolve-token.js","sourceRoot":"","sources":["../../src/auth/resolve-token.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,eAAe,EACf,cAAc,GACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;IACvC,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;IACjC,IAAI,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,OAAO,MAAM,CAAC,WAAW,CAAC;IAC5B,CAAC;IAED,uDAAuD;IACvD,mEAAmE;IACnE,wDAAwD;IACxD,+CAA+C;IAC/C,MAAM,OAAO,GAAG,MAAM,KAAK,IAAI,CAAC;IAChC,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IACjD,eAAe,CAAC,KAAK,CAAC,CAAC;IACvB,OAAO,KAAK,CAAC,WAAW,CAAC;AAC3B,CAAC"}
package/dist/config.d.ts CHANGED
@@ -2,5 +2,4 @@ export declare const DEFAULT_API_URL = "https://mcp.workshop.institute";
2
2
  export declare function apiUrl(): string;
3
3
  export declare function mcpEndpoint(): string;
4
4
  export declare function landingUrl(): string;
5
- export declare function tokenExchangeUrl(): string;
6
5
  export declare function cliAuthPageUrl(): string;
package/dist/config.js CHANGED
@@ -18,9 +18,6 @@ export function landingUrl() {
18
18
  const host = api.host.replace(/^mcp[.-]/, '');
19
19
  return `${api.protocol}//${host}`;
20
20
  }
21
- export function tokenExchangeUrl() {
22
- return `${apiUrl()}/cli/token`;
23
- }
24
21
  export function cliAuthPageUrl() {
25
22
  return `${landingUrl()}/cli-auth`;
26
23
  }
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG,gCAAgC,CAAC;AAEhE,MAAM,UAAU,MAAM;IACpB,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,eAAe,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,GAAG,MAAM,EAAE,MAAM,CAAC;AAC3B,CAAC;AAED,wEAAwE;AACxE,oEAAoE;AACpE,4DAA4D;AAC5D,gEAAgE;AAChE,wDAAwD;AACxD,MAAM,UAAU,UAAU;IACxB,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IAC7C,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9B,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC9C,OAAO,GAAG,GAAG,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,GAAG,MAAM,EAAE,YAAY,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,GAAG,UAAU,EAAE,WAAW,CAAC;AACpC,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG,gCAAgC,CAAC;AAEhE,MAAM,UAAU,MAAM;IACpB,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,eAAe,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,GAAG,MAAM,EAAE,MAAM,CAAC;AAC3B,CAAC;AAED,wEAAwE;AACxE,oEAAoE;AACpE,4DAA4D;AAC5D,gEAAgE;AAChE,wDAAwD;AACxD,MAAM,UAAU,UAAU;IACxB,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IAC7C,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9B,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC9C,OAAO,GAAG,GAAG,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,GAAG,UAAU,EAAE,WAAW,CAAC;AACpC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@learning-with-court/cli",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "CLI for learning-with-court — proxies MCP and bootstraps workshop projects.",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",
@@ -1,6 +0,0 @@
1
- export declare function generateState(): string;
2
- export interface PkcePair {
3
- codeVerifier: string;
4
- codeChallenge: string;
5
- }
6
- export declare function generatePkcePair(): PkcePair;
package/dist/auth/pkce.js DELETED
@@ -1,13 +0,0 @@
1
- import { createHash, randomBytes } from 'node:crypto';
2
- // 32 random bytes → 43-char base64url string. Matches the inputs the
3
- // platform's /cli/authorize and /cli/token endpoints validate against
4
- // (see docs/features/own-cli-auth-flow/plan.md, Phase 1).
5
- export function generateState() {
6
- return randomBytes(32).toString('base64url');
7
- }
8
- export function generatePkcePair() {
9
- const codeVerifier = randomBytes(32).toString('base64url');
10
- const codeChallenge = createHash('sha256').update(codeVerifier).digest('base64url');
11
- return { codeVerifier, codeChallenge };
12
- }
13
- //# sourceMappingURL=pkce.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"pkce.js","sourceRoot":"","sources":["../../src/auth/pkce.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEtD,qEAAqE;AACrE,sEAAsE;AACtE,0DAA0D;AAE1D,MAAM,UAAU,aAAa;IAC3B,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAC/C,CAAC;AAOD,MAAM,UAAU,gBAAgB;IAC9B,MAAM,YAAY,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC3D,MAAM,aAAa,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IACpF,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC;AACzC,CAAC"}