sitevision-cli 1.0.0-beta.11 → 1.0.0-beta.12

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,10 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
2
+ import { useState, useEffect } from 'react';
3
3
  import { Box, Text, useInput } from 'ink';
4
4
  import { TextInput } from './TextInput.js';
5
5
  import { writeDevProperties } from '../utils/project-detection.js';
6
6
  import { setDeployPassword, deleteDeployPassword, setOAuth2ClientSecret, } from '../utils/keychain.js';
7
- import { DEFAULT_REDIRECT_PORT } from '../utils/oauth2-auth.js';
7
+ import { DEFAULT_REDIRECT_PORT, discoverOAuth2Config, } from '../utils/oauth2-auth.js';
8
8
  const LABELS = {
9
9
  domain: 'Domain',
10
10
  siteName: 'Site Name',
@@ -50,8 +50,39 @@ export function DevPropertiesForm({ projectRoot, initialProperties, packageJson,
50
50
  scopes: initialProperties?.oauth2?.scopes?.join(' ') ?? '',
51
51
  clientSecret: '',
52
52
  }));
53
+ const [discoveryNote, setDiscoveryNote] = useState('');
53
54
  const method = properties.authMethod ?? 'basic';
54
55
  const isOAuth = method === 'oauth2';
56
+ // When OAuth2 is chosen, auto-fill the endpoints from the site's OpenID
57
+ // configuration (unauthenticated) so the user doesn't type them in.
58
+ useEffect(() => {
59
+ if (method !== 'oauth2' || !properties.domain)
60
+ return;
61
+ if (oauth.authorizationEndpoint && oauth.tokenEndpoint)
62
+ return;
63
+ let cancelled = false;
64
+ setDiscoveryNote('Looking up OAuth2 endpoints…');
65
+ void discoverOAuth2Config(properties.domain, properties.useHTTPForDevDeploy).then(discovered => {
66
+ if (cancelled)
67
+ return;
68
+ if (discovered) {
69
+ setOauth(previous => ({
70
+ ...previous,
71
+ authorizationEndpoint: previous.authorizationEndpoint || discovered.authorizationEndpoint,
72
+ tokenEndpoint: previous.tokenEndpoint || discovered.tokenEndpoint,
73
+ scopes: previous.scopes || (discovered.scopesSupported?.join(' ') ?? ''),
74
+ }));
75
+ setDiscoveryNote('Endpoints auto-filled from the site OpenID config.');
76
+ }
77
+ else {
78
+ setDiscoveryNote('Could not auto-discover endpoints — enter them below.');
79
+ }
80
+ });
81
+ return () => {
82
+ cancelled = true;
83
+ };
84
+ // eslint-disable-next-line react-hooks/exhaustive-deps
85
+ }, [method, properties.domain]);
55
86
  const methodSteps = method === 'oauth2'
56
87
  ? [
57
88
  'oauthClientId',
@@ -177,7 +208,7 @@ export function DevPropertiesForm({ projectRoot, initialProperties, packageJson,
177
208
  return null;
178
209
  }
179
210
  };
180
- return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "Setup Development Properties" }), _jsxs(Text, { children: [' ', "Step ", stepIndex + 1, " of ", steps.length, ":", ' ', currentStep ? LABELS[currentStep] : ''] })] }), isOAuth && (_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { dimColor: true, children: ["Whitelist this redirect URI on the OAuth2 client: http://127.0.0.1:", redirectPort, "/callback"] }) })), _jsx(Box, { marginBottom: 1, children: steps.map((s, i) => (_jsx(Box, { marginRight: 1, children: _jsx(Text, { color: i <= stepIndex ? 'green' : 'gray', children: i < stepIndex ? '✓' : i === stepIndex ? '●' : '○' }) }, s))) }), _jsx(Box, { borderStyle: "single", borderColor: "gray", padding: 1, children: renderInput() })] }));
211
+ return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "Setup Development Properties" }), _jsxs(Text, { children: [' ', "Step ", stepIndex + 1, " of ", steps.length, ":", ' ', currentStep ? LABELS[currentStep] : ''] })] }), isOAuth && (_jsxs(Box, { marginBottom: 1, flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: ["Whitelist this redirect URI on the OAuth2 client: http://127.0.0.1:", redirectPort, "/callback"] }), discoveryNote && _jsx(Text, { color: "yellow", children: discoveryNote })] })), _jsx(Box, { marginBottom: 1, children: steps.map((s, i) => (_jsx(Box, { marginRight: 1, children: _jsx(Text, { color: i <= stepIndex ? 'green' : 'gray', children: i < stepIndex ? '✓' : i === stepIndex ? '●' : '○' }) }, s))) }), _jsx(Box, { borderStyle: "single", borderColor: "gray", padding: 1, children: renderInput() })] }));
181
212
  }
182
213
  function MethodSelect({ defaultValue, onSubmit, }) {
183
214
  const [index, setIndex] = useState(() => Math.max(0, AUTH_METHODS.findIndex(m => m.value === defaultValue)));
@@ -6,6 +6,18 @@ export declare function createPkcePair(): {
6
6
  verifier: string;
7
7
  challenge: string;
8
8
  };
9
+ export interface DiscoveredOAuth2 {
10
+ authorizationEndpoint: string;
11
+ tokenEndpoint: string;
12
+ scopesSupported?: string[];
13
+ }
14
+ /**
15
+ * Fetch the site's OpenID configuration (unauthenticated) to auto-fill the
16
+ * authorization/token endpoints. Returns null if it isn't published (provider
17
+ * not enabled) or the response isn't a valid config, so callers fall back to
18
+ * manual entry.
19
+ */
20
+ export declare function discoverOAuth2Config(domain: string, useHTTP?: boolean): Promise<DiscoveredOAuth2 | null>;
9
21
  export declare function openBrowser(url: string): void;
10
22
  /**
11
23
  * Start an interactive OAuth2 login. Returns the authorize URL to open and a
@@ -44,6 +44,37 @@ async function postToken(config, params, secret) {
44
44
  return null;
45
45
  }
46
46
  }
47
+ /** OpenID configuration path (published at the issuer root once the provider is saved). */
48
+ const DISCOVERY_PATH = '/.well-known/openid-configuration';
49
+ /**
50
+ * Fetch the site's OpenID configuration (unauthenticated) to auto-fill the
51
+ * authorization/token endpoints. Returns null if it isn't published (provider
52
+ * not enabled) or the response isn't a valid config, so callers fall back to
53
+ * manual entry.
54
+ */
55
+ export async function discoverOAuth2Config(domain, useHTTP = false) {
56
+ if (!domain)
57
+ return null;
58
+ const protocol = useHTTP ? 'http' : 'https';
59
+ try {
60
+ const response = await makeRequest(`${protocol}://${domain}${DISCOVERY_PATH}`, { method: 'GET' });
61
+ if (response.statusCode !== 200)
62
+ return null;
63
+ const doc = JSON.parse(response.body.toString());
64
+ if (!doc.authorization_endpoint || !doc.token_endpoint)
65
+ return null;
66
+ return {
67
+ authorizationEndpoint: doc.authorization_endpoint,
68
+ tokenEndpoint: doc.token_endpoint,
69
+ scopesSupported: Array.isArray(doc.scopes_supported)
70
+ ? doc.scopes_supported
71
+ : undefined,
72
+ };
73
+ }
74
+ catch {
75
+ return null;
76
+ }
77
+ }
47
78
  export function openBrowser(url) {
48
79
  const isWin = process.platform === 'win32';
49
80
  const cmd = process.platform === 'darwin' ? 'open' : isWin ? 'cmd' : 'xdg-open';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sitevision-cli",
3
- "version": "1.0.0-beta.11",
3
+ "version": "1.0.0-beta.12",
4
4
  "license": "MIT",
5
5
  "bin": {
6
6
  "svc": "dist/cli.js"