sitevision-cli 1.0.0-beta.0 → 1.0.0-beta.10
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/app.d.ts +1 -1
- package/dist/app.js +44 -7
- package/dist/cli.js +22 -0
- package/dist/commands/deploy.d.ts +1 -2
- package/dist/commands/deploy.js +96 -22
- package/dist/commands/dev.js +29 -3
- package/dist/commands/info.js +2 -2
- package/dist/components/AuthLoginScreen.d.ts +21 -0
- package/dist/components/AuthLoginScreen.js +87 -0
- package/dist/components/DevPropertiesForm.js +160 -31
- package/dist/components/InfoScreen.js +2 -2
- package/dist/components/MainMenu.js +2 -2
- package/dist/components/SetupFlow.d.ts +2 -1
- package/dist/components/SetupFlow.js +100 -11
- package/dist/types/index.d.ts +32 -3
- package/dist/utils/jsonc.d.ts +19 -0
- package/dist/utils/jsonc.js +74 -0
- package/dist/utils/keychain.d.ts +9 -0
- package/dist/utils/keychain.js +54 -0
- package/dist/utils/oauth2-auth.d.ts +27 -0
- package/dist/utils/oauth2-auth.js +172 -0
- package/dist/utils/password-prompt.d.ts +5 -0
- package/dist/utils/password-prompt.js +28 -0
- package/dist/utils/project-detection.d.ts +56 -3
- package/dist/utils/project-detection.js +179 -21
- package/dist/utils/session-cookie-auth.d.ts +34 -0
- package/dist/utils/session-cookie-auth.js +99 -0
- package/dist/utils/sitevision-api.d.ts +29 -5
- package/dist/utils/sitevision-api.js +121 -32
- package/package.json +2 -1
- package/readme.md +23 -0
|
@@ -3,15 +3,35 @@ import { useState } 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
|
-
import { setDeployPassword, deleteDeployPassword } from '../utils/keychain.js';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
import { setDeployPassword, deleteDeployPassword, setOAuth2ClientSecret, } from '../utils/keychain.js';
|
|
7
|
+
import { DEFAULT_REDIRECT_PORT } from '../utils/oauth2-auth.js';
|
|
8
|
+
const LABELS = {
|
|
9
|
+
domain: 'Domain',
|
|
10
|
+
siteName: 'Site Name',
|
|
11
|
+
addonName: 'Addon Name',
|
|
12
|
+
username: 'Username',
|
|
13
|
+
authMethod: 'Auth Method',
|
|
14
|
+
password: 'Password',
|
|
15
|
+
oauthClientId: 'Client ID',
|
|
16
|
+
oauthAuthEndpoint: 'Authorization Endpoint',
|
|
17
|
+
oauthTokenEndpoint: 'Token Endpoint',
|
|
18
|
+
oauthScopes: 'Scopes',
|
|
19
|
+
oauthClientSecret: 'Client Secret',
|
|
20
|
+
sessionLoginUrl: 'Login URL',
|
|
21
|
+
useHTTP: 'Use HTTP',
|
|
22
|
+
};
|
|
23
|
+
const AUTH_METHODS = [
|
|
24
|
+
{ value: 'basic', label: 'Basic auth (username + password)' },
|
|
25
|
+
{ value: 'oauth2', label: 'OAuth2 bearer token (PKCE)' },
|
|
26
|
+
{ value: 'cookie', label: 'Session cookie (SAML / SSO login)' },
|
|
14
27
|
];
|
|
28
|
+
function parseScopes(raw) {
|
|
29
|
+
const scopes = raw
|
|
30
|
+
.split(/[\s,]+/)
|
|
31
|
+
.map(s => s.trim())
|
|
32
|
+
.filter(Boolean);
|
|
33
|
+
return scopes.length > 0 ? scopes : undefined;
|
|
34
|
+
}
|
|
15
35
|
export function DevPropertiesForm({ projectRoot, initialProperties, packageJson, onComplete, onCancel, }) {
|
|
16
36
|
const [stepIndex, setStepIndex] = useState(0);
|
|
17
37
|
const [properties, setProperties] = useState(() => {
|
|
@@ -19,49 +39,158 @@ export function DevPropertiesForm({ projectRoot, initialProperties, packageJson,
|
|
|
19
39
|
domain: packageJson.developmentDomain || '',
|
|
20
40
|
addonName: packageJson.addonName || '',
|
|
21
41
|
siteName: packageJson.siteName || '',
|
|
42
|
+
authMethod: 'basic',
|
|
22
43
|
};
|
|
23
44
|
return { ...defaults, ...initialProperties };
|
|
24
45
|
});
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
46
|
+
const [oauth, setOauth] = useState(() => ({
|
|
47
|
+
authorizationEndpoint: initialProperties?.oauth2?.authorizationEndpoint ?? '',
|
|
48
|
+
tokenEndpoint: initialProperties?.oauth2?.tokenEndpoint ?? '',
|
|
49
|
+
clientId: initialProperties?.oauth2?.clientId ?? '',
|
|
50
|
+
scopes: initialProperties?.oauth2?.scopes?.join(' ') ?? '',
|
|
51
|
+
clientSecret: '',
|
|
52
|
+
}));
|
|
53
|
+
const method = properties.authMethod ?? 'basic';
|
|
54
|
+
const isOAuth = method === 'oauth2';
|
|
55
|
+
const methodSteps = method === 'oauth2'
|
|
56
|
+
? [
|
|
57
|
+
'oauthClientId',
|
|
58
|
+
'oauthAuthEndpoint',
|
|
59
|
+
'oauthTokenEndpoint',
|
|
60
|
+
'oauthScopes',
|
|
61
|
+
'oauthClientSecret',
|
|
62
|
+
]
|
|
63
|
+
: method === 'cookie'
|
|
64
|
+
? ['sessionLoginUrl']
|
|
65
|
+
: ['password'];
|
|
66
|
+
const steps = [
|
|
67
|
+
'domain',
|
|
68
|
+
'siteName',
|
|
69
|
+
'addonName',
|
|
70
|
+
'username',
|
|
71
|
+
'authMethod',
|
|
72
|
+
...methodSteps,
|
|
73
|
+
'useHTTP',
|
|
74
|
+
];
|
|
75
|
+
const currentStep = steps[stepIndex];
|
|
76
|
+
const redirectPort = initialProperties?.oauth2?.redirectPort ?? DEFAULT_REDIRECT_PORT;
|
|
77
|
+
const advance = () => {
|
|
78
|
+
if (stepIndex < steps.length - 1) {
|
|
30
79
|
setStepIndex(stepIndex + 1);
|
|
31
80
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
81
|
+
};
|
|
82
|
+
const finalize = (props, fields) => {
|
|
83
|
+
const authMethod = props.authMethod ?? 'basic';
|
|
84
|
+
const domain = props.domain ?? '';
|
|
85
|
+
const username = props.username ?? '';
|
|
86
|
+
const finalProps = {
|
|
87
|
+
domain,
|
|
88
|
+
siteName: props.siteName ?? '',
|
|
89
|
+
addonName: props.addonName ?? '',
|
|
90
|
+
username,
|
|
91
|
+
authMethod,
|
|
92
|
+
useHTTPForDevDeploy: props.useHTTPForDevDeploy ?? false,
|
|
93
|
+
};
|
|
94
|
+
if (authMethod === 'oauth2') {
|
|
95
|
+
finalProps.oauth2 = {
|
|
96
|
+
authorizationEndpoint: fields.authorizationEndpoint,
|
|
97
|
+
tokenEndpoint: fields.tokenEndpoint,
|
|
98
|
+
clientId: fields.clientId,
|
|
99
|
+
scopes: parseScopes(fields.scopes),
|
|
100
|
+
...(initialProperties?.oauth2?.redirectPort && {
|
|
101
|
+
redirectPort: initialProperties.oauth2.redirectPort,
|
|
102
|
+
}),
|
|
103
|
+
};
|
|
104
|
+
if (fields.clientSecret && domain && fields.clientId) {
|
|
105
|
+
setOAuth2ClientSecret(domain, fields.clientId, fields.clientSecret);
|
|
37
106
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
107
|
+
}
|
|
108
|
+
else if (authMethod === 'cookie') {
|
|
109
|
+
if (props.sessionLoginUrl) {
|
|
110
|
+
finalProps.sessionLoginUrl = props.sessionLoginUrl;
|
|
41
111
|
}
|
|
42
|
-
|
|
43
|
-
|
|
112
|
+
}
|
|
113
|
+
else if (props.password && domain && username) {
|
|
114
|
+
setDeployPassword(domain, username, props.password);
|
|
115
|
+
}
|
|
116
|
+
else if (domain && username) {
|
|
117
|
+
// Empty password — clear any stale keychain entry so deploy prompts.
|
|
118
|
+
deleteDeployPassword(domain, username);
|
|
119
|
+
}
|
|
120
|
+
writeDevProperties(projectRoot, finalProps);
|
|
121
|
+
onComplete();
|
|
122
|
+
};
|
|
123
|
+
// Update a top-level DevProperties field, then advance or finalize.
|
|
124
|
+
const submitProperty = (key, value) => {
|
|
125
|
+
const next = { ...properties, [key]: value };
|
|
126
|
+
setProperties(next);
|
|
127
|
+
if (steps[stepIndex] === steps[steps.length - 1]) {
|
|
128
|
+
finalize(next, oauth);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
advance();
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
// Update an OAuth field, then advance or finalize.
|
|
135
|
+
const submitOAuth = (key, value) => {
|
|
136
|
+
const next = { ...oauth, [key]: value };
|
|
137
|
+
setOauth(next);
|
|
138
|
+
if (steps[stepIndex] === steps[steps.length - 1]) {
|
|
139
|
+
finalize(properties, next);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
advance();
|
|
44
143
|
}
|
|
45
144
|
};
|
|
46
145
|
const renderInput = () => {
|
|
47
|
-
switch (currentStep
|
|
146
|
+
switch (currentStep) {
|
|
48
147
|
case 'domain':
|
|
49
|
-
return (_jsx(TextInput, { label: "Development Domain (e.g. www.sitevision.se)", defaultValue: properties.domain, placeholder: "sitevision.se", onSubmit:
|
|
148
|
+
return (_jsx(TextInput, { label: "Development Domain (e.g. www.sitevision.se)", defaultValue: properties.domain, placeholder: "sitevision.se", onSubmit: value => submitProperty('domain', value), onCancel: onCancel }, "domain"));
|
|
50
149
|
case 'siteName':
|
|
51
|
-
return (_jsx(TextInput, { label: "Site Name (Root node name)", defaultValue: properties.siteName, onSubmit:
|
|
150
|
+
return (_jsx(TextInput, { label: "Site Name (Root node name)", defaultValue: properties.siteName, onSubmit: value => submitProperty('siteName', value), onCancel: onCancel }, "siteName"));
|
|
52
151
|
case 'addonName':
|
|
53
|
-
return (_jsx(TextInput, { label: "Addon Name", defaultValue: properties.addonName, onSubmit:
|
|
152
|
+
return (_jsx(TextInput, { label: "Addon Name", defaultValue: properties.addonName, onSubmit: value => submitProperty('addonName', value), onCancel: onCancel }, "addonName"));
|
|
54
153
|
case 'username':
|
|
55
|
-
return (_jsx(TextInput, { label: "Username (usually your Sitevision Cloud email)", defaultValue: properties.username, onSubmit:
|
|
154
|
+
return (_jsx(TextInput, { label: "Username (usually your Sitevision Cloud email)", defaultValue: properties.username, onSubmit: value => submitProperty('username', value), onCancel: onCancel }, "username"));
|
|
155
|
+
case 'authMethod':
|
|
156
|
+
return (_jsx(MethodSelect, { defaultValue: method, onSubmit: value => submitProperty('authMethod', value) }, "authMethod"));
|
|
56
157
|
case 'password':
|
|
57
|
-
return (_jsx(TextInput, { label: "Password (saved in OS keychain \u2014 leave empty to prompt on each run)", type: "password", defaultValue: properties.password, onSubmit:
|
|
158
|
+
return (_jsx(TextInput, { label: "Password (saved in OS keychain \u2014 leave empty to prompt on each run)", type: "password", defaultValue: properties.password, onSubmit: value => submitProperty('password', value), onCancel: onCancel }, "password"));
|
|
159
|
+
case 'oauthClientId':
|
|
160
|
+
return (_jsx(TextInput, { label: "OAuth2 Client ID", defaultValue: oauth.clientId, onSubmit: value => submitOAuth('clientId', value), onCancel: onCancel }, "oauthClientId"));
|
|
161
|
+
case 'oauthAuthEndpoint':
|
|
162
|
+
return (_jsx(TextInput, { label: "Authorization Endpoint URL", defaultValue: oauth.authorizationEndpoint, onSubmit: value => submitOAuth('authorizationEndpoint', value), onCancel: onCancel }, "oauthAuthEndpoint"));
|
|
163
|
+
case 'oauthTokenEndpoint':
|
|
164
|
+
return (_jsx(TextInput, { label: "Token Endpoint URL", defaultValue: oauth.tokenEndpoint, onSubmit: value => submitOAuth('tokenEndpoint', value), onCancel: onCancel }, "oauthTokenEndpoint"));
|
|
165
|
+
case 'oauthScopes':
|
|
166
|
+
return (_jsx(TextInput, { label: "Scopes (space-separated, optional)", defaultValue: oauth.scopes, onSubmit: value => submitOAuth('scopes', value), onCancel: onCancel }, "oauthScopes"));
|
|
167
|
+
case 'oauthClientSecret':
|
|
168
|
+
return (_jsx(TextInput, { label: "Client Secret (OS keychain \u2014 leave empty for a public/PKCE client)", type: "password", defaultValue: oauth.clientSecret, onSubmit: value => submitOAuth('clientSecret', value), onCancel: onCancel }, "oauthClientSecret"));
|
|
169
|
+
case 'sessionLoginUrl':
|
|
170
|
+
return (_jsx(TextInput, { label: "Login URL (opened in a browser; blank = site root)", defaultValue: properties.sessionLoginUrl ??
|
|
171
|
+
(properties.domain ? `https://${properties.domain}/` : ''), onSubmit: value => submitProperty('sessionLoginUrl', value), onCancel: onCancel }, "sessionLoginUrl"));
|
|
58
172
|
case 'useHTTP':
|
|
59
|
-
return (_jsx(BooleanInput, { label: "Use HTTP for deployment? (y/n)", defaultValue: properties.useHTTPForDevDeploy, onSubmit:
|
|
173
|
+
return (_jsx(BooleanInput, { label: "Use HTTP for deployment? (y/n)", defaultValue: properties.useHTTPForDevDeploy ?? false, onSubmit: value => submitProperty('useHTTPForDevDeploy', value) }, "useHTTP"));
|
|
60
174
|
default:
|
|
61
175
|
return null;
|
|
62
176
|
}
|
|
63
177
|
};
|
|
64
|
-
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 ",
|
|
178
|
+
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() })] }));
|
|
179
|
+
}
|
|
180
|
+
function MethodSelect({ defaultValue, onSubmit, }) {
|
|
181
|
+
const [index, setIndex] = useState(() => Math.max(0, AUTH_METHODS.findIndex(m => m.value === defaultValue)));
|
|
182
|
+
useInput((_input, key) => {
|
|
183
|
+
if (key.upArrow) {
|
|
184
|
+
setIndex(p => (p === 0 ? AUTH_METHODS.length - 1 : p - 1));
|
|
185
|
+
}
|
|
186
|
+
else if (key.downArrow) {
|
|
187
|
+
setIndex(p => (p === AUTH_METHODS.length - 1 ? 0 : p + 1));
|
|
188
|
+
}
|
|
189
|
+
else if (key.return) {
|
|
190
|
+
onSubmit(AUTH_METHODS[index].value);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Authentication method" }) }), AUTH_METHODS.map((m, i) => (_jsxs(Text, { color: i === index ? 'green' : undefined, children: [i === index ? '❯ ' : ' ', m.label] }, m.value))), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "\u2191/\u2193 to move, Enter to select" }) })] }));
|
|
65
194
|
}
|
|
66
195
|
function BooleanInput({ label, defaultValue, onSubmit, }) {
|
|
67
196
|
useInput(input => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text, useInput } from 'ink';
|
|
3
|
-
import { getAppType } from '../utils/project-detection.js';
|
|
3
|
+
import { getAppType, localizedText, } from '../utils/project-detection.js';
|
|
4
4
|
import { checkSitevisionScriptsCompatibility } from '../utils/sitevision-scripts-runner.js';
|
|
5
5
|
export function InfoScreen({ project, onBack }) {
|
|
6
6
|
const appType = getAppType(project.manifest);
|
|
@@ -10,5 +10,5 @@ export function InfoScreen({ project, onBack }) {
|
|
|
10
10
|
onBack();
|
|
11
11
|
}
|
|
12
12
|
});
|
|
13
|
-
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Sitevision Project Information" }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Name: " }), _jsx(Text, { children: project.manifest.name })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "ID: " }), _jsx(Text, { children: project.manifest.id })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Version: " }), _jsx(Text, { children: project.manifest.version })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Type: " }), _jsx(Text, { color: "green", children: project.manifest.type }), _jsxs(Text, { dimColor: true, children: [" (", appType, ")"] })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Bundled: " }), _jsx(Text, { children: project.manifest.bundled ? 'Yes' : 'No' })] })] }), _jsx(Box, { marginTop: 1, marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Build Tooling" }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: "sitevision-scripts: " }), _jsx(Text, { children: scriptsCompat.installed ?? 'not installed' }), _jsxs(Text, { dimColor: true, children: [" (supported ", scriptsCompat.supportedRange, ")"] })] }), scriptsCompat.warning && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "yellow", children: ["\u26A0 ", scriptsCompat.warning] }) }))] }), project.hasDevProperties && project.devProperties && (_jsxs(_Fragment, { children: [_jsx(Box, { marginTop: 1, marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Development Configuration" }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Domain: " }), _jsx(Text, { children: project.devProperties.domain })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Site: " }), _jsx(Text, { children: project.devProperties.siteName })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Addon: " }), _jsx(Text, { children: project.devProperties.addonName })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Username: " }), _jsx(Text, { children: project.devProperties.username })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Use HTTP: " }), _jsx(Text, { children: project.devProperties.useHTTPForDevDeploy ? 'Yes' : 'No' })] })] })] })), !project.hasDevProperties && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "yellow", children: "\u26A0 No dev properties found. Run setup-dev-properties to configure." }) })), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { bold: true, children: "Project Root: " }), _jsx(Text, { dimColor: true, children: project.root })] }), _jsx(Box, { marginTop: 2, children: _jsx(Text, { dimColor: true, children: "Press Enter or ESC to return to menu" }) })] }));
|
|
13
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Sitevision Project Information" }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Name: " }), _jsx(Text, { children: localizedText(project.manifest.name) })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "ID: " }), _jsx(Text, { children: project.manifest.id })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Version: " }), _jsx(Text, { children: project.manifest.version })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Type: " }), _jsx(Text, { color: "green", children: project.manifest.type }), _jsxs(Text, { dimColor: true, children: [" (", appType, ")"] })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Bundled: " }), _jsx(Text, { children: project.manifest.bundled ? 'Yes' : 'No' })] })] }), _jsx(Box, { marginTop: 1, marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Build Tooling" }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: "sitevision-scripts: " }), _jsx(Text, { children: scriptsCompat.installed ?? 'not installed' }), _jsxs(Text, { dimColor: true, children: [" (supported ", scriptsCompat.supportedRange, ")"] })] }), scriptsCompat.warning && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "yellow", children: ["\u26A0 ", scriptsCompat.warning] }) }))] }), project.hasDevProperties && project.devProperties && (_jsxs(_Fragment, { children: [_jsx(Box, { marginTop: 1, marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Development Configuration" }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Domain: " }), _jsx(Text, { children: project.devProperties.domain })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Site: " }), _jsx(Text, { children: project.devProperties.siteName })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Addon: " }), _jsx(Text, { children: project.devProperties.addonName })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Username: " }), _jsx(Text, { children: project.devProperties.username })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Use HTTP: " }), _jsx(Text, { children: project.devProperties.useHTTPForDevDeploy ? 'Yes' : 'No' })] })] })] })), !project.hasDevProperties && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "yellow", children: "\u26A0 No dev properties found. Run setup-dev-properties to configure." }) })), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { bold: true, children: "Project Root: " }), _jsx(Text, { dimColor: true, children: project.root })] }), _jsx(Box, { marginTop: 2, children: _jsx(Text, { dimColor: true, children: "Press Enter or ESC to return to menu" }) })] }));
|
|
14
14
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import { Box, Text, useInput } from 'ink';
|
|
4
|
-
import { getAppType } from '../utils/project-detection.js';
|
|
4
|
+
import { getAppType, localizedText } from '../utils/project-detection.js';
|
|
5
5
|
export function MainMenu({ project, onSelect }) {
|
|
6
6
|
const appType = getAppType(project.manifest);
|
|
7
7
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
@@ -79,5 +79,5 @@ export function MainMenu({ project, onSelect }) {
|
|
|
79
79
|
onSelect(selectedItem.value);
|
|
80
80
|
}
|
|
81
81
|
});
|
|
82
|
-
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Sitevision Project Information" }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 2, marginBottom: 1, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Name: " }), _jsx(Text, { children: project.manifest.name })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "ID: " }), _jsx(Text, { children: project.manifest.id })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Version: " }), _jsx(Text, { children: project.manifest.version })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Type: " }), _jsx(Text, { color: "green", children: project.manifest.type }), _jsxs(Text, { dimColor: true, children: [" (", appType, ")"] })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Bundled: " }), _jsx(Text, { children: project.manifest.bundled ? 'Yes' : 'No' })] })] }), project.hasDevProperties && project.devProperties && (_jsxs(_Fragment, { children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Development Configuration" }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 2, marginBottom: 1, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Domain: " }), _jsx(Text, { children: project.devProperties.domain })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Site: " }), _jsx(Text, { children: project.devProperties.siteName })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Addon: " }), _jsx(Text, { children: project.devProperties.addonName })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Username: " }), _jsx(Text, { children: project.devProperties.username })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Use HTTP: " }), _jsx(Text, { children: project.devProperties.useHTTPForDevDeploy ? 'Yes' : 'No' })] })] })] })), project.hasSigningProperties && project.devProperties && (_jsxs(_Fragment, { children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Signing Configuration" }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 2, marginBottom: 1, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Signing User: " }), _jsx(Text, { children: project.devProperties.signingUsername })] }), project.devProperties.certificateName && (_jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Certificate: " }), _jsx(Text, { children: project.devProperties.certificateName })] }))] })] })), !project.hasDevProperties && (_jsx(Box, { marginBottom: 1, paddingX: 1, borderStyle: "round", borderColor: "yellow", children: _jsx(Text, { color: "yellow", children: "\u26A0 No dev properties found. Some commands may not work." }) })), project.hasDevProperties && !project.hasSigningProperties && (_jsx(Box, { marginBottom: 1, paddingX: 1, borderStyle: "round", borderColor: "yellow", children: _jsx(Text, { color: "yellow", children: "\u26A0 No signing credentials configured. Run svc setup-signing to configure." }) })), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { dimColor: true, children: "Select a command:" }) }), _jsx(Box, { flexDirection: "column", children: items.map((item, index) => (_jsx(Box, { marginLeft: 1, children: _jsxs(Text, { color: index === selectedIndex ? 'cyan' : undefined, bold: index === selectedIndex, children: [index === selectedIndex ? '▶ ' : ' ', item.label, item.description && _jsxs(Text, { dimColor: true, children: [" - ", item.description] })] }) }, item.value))) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Use \u2191\u2193 arrows to navigate, Enter to select" }) })] }));
|
|
82
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Sitevision Project Information" }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 2, marginBottom: 1, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Name: " }), _jsx(Text, { children: localizedText(project.manifest.name) })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "ID: " }), _jsx(Text, { children: project.manifest.id })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Version: " }), _jsx(Text, { children: project.manifest.version })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Type: " }), _jsx(Text, { color: "green", children: project.manifest.type }), _jsxs(Text, { dimColor: true, children: [" (", appType, ")"] })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Bundled: " }), _jsx(Text, { children: project.manifest.bundled ? 'Yes' : 'No' })] })] }), project.hasDevProperties && project.devProperties && (_jsxs(_Fragment, { children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Development Configuration" }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 2, marginBottom: 1, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Domain: " }), _jsx(Text, { children: project.devProperties.domain })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Site: " }), _jsx(Text, { children: project.devProperties.siteName })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Addon: " }), _jsx(Text, { children: project.devProperties.addonName })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Username: " }), _jsx(Text, { children: project.devProperties.username })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Use HTTP: " }), _jsx(Text, { children: project.devProperties.useHTTPForDevDeploy ? 'Yes' : 'No' })] })] })] })), project.hasSigningProperties && project.devProperties && (_jsxs(_Fragment, { children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Signing Configuration" }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 2, marginBottom: 1, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Signing User: " }), _jsx(Text, { children: project.devProperties.signingUsername })] }), project.devProperties.certificateName && (_jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Certificate: " }), _jsx(Text, { children: project.devProperties.certificateName })] }))] })] })), !project.hasDevProperties && (_jsx(Box, { marginBottom: 1, paddingX: 1, borderStyle: "round", borderColor: "yellow", children: _jsx(Text, { color: "yellow", children: "\u26A0 No dev properties found. Some commands may not work." }) })), project.hasDevProperties && !project.hasSigningProperties && (_jsx(Box, { marginBottom: 1, paddingX: 1, borderStyle: "round", borderColor: "yellow", children: _jsx(Text, { color: "yellow", children: "\u26A0 No signing credentials configured. Run svc setup-signing to configure." }) })), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { dimColor: true, children: "Select a command:" }) }), _jsx(Box, { flexDirection: "column", children: items.map((item, index) => (_jsx(Box, { marginLeft: 1, children: _jsxs(Text, { color: index === selectedIndex ? 'cyan' : undefined, bold: index === selectedIndex, children: [index === selectedIndex ? '▶ ' : ' ', item.label, item.description && _jsxs(Text, { dimColor: true, children: [" - ", item.description] })] }) }, item.value))) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Use \u2191\u2193 arrows to navigate, Enter to select" }) })] }));
|
|
83
83
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { type ProjectInfo } from '../utils/project-detection.js';
|
|
2
2
|
interface Props {
|
|
3
3
|
project: ProjectInfo;
|
|
4
|
+
onReload: () => void;
|
|
4
5
|
onComplete: () => void;
|
|
5
6
|
}
|
|
6
|
-
export declare function SetupFlow({ project, onComplete }: Props): import("react").JSX.Element | null;
|
|
7
|
+
export declare function SetupFlow({ project, onReload, onComplete }: Props): import("react").JSX.Element | null;
|
|
7
8
|
export {};
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useState, useEffect } from 'react';
|
|
3
3
|
import { Box, Text, useInput } from 'ink';
|
|
4
|
-
import { getAppType, migrateLegacyPassword, } from '../utils/project-detection.js';
|
|
4
|
+
import { getAppType, localizedText, migrateLegacyPassword, getPackageJsonSyncChanges, syncDevPropertiesToPackageJson, readSvcConfig, writeSvcConfig, writeDevProperties, } from '../utils/project-detection.js';
|
|
5
5
|
import { ProcessRunner } from '../utils/process-runner.js';
|
|
6
6
|
import { ProcessOutputComponent } from './ProcessOutput.js';
|
|
7
7
|
import { StatusIndicator } from './StatusIndicator.js';
|
|
8
8
|
import { DevPropertiesForm } from './DevPropertiesForm.js';
|
|
9
9
|
import { SigningPropertiesForm } from './SigningPropertiesForm.js';
|
|
10
|
-
export function SetupFlow({ project, onComplete }) {
|
|
10
|
+
export function SetupFlow({ project, onReload, onComplete }) {
|
|
11
11
|
const [step, setStep] = useState('check-node-modules');
|
|
12
12
|
const [runner, setRunner] = useState(null);
|
|
13
13
|
const [commandStatus, setCommandStatus] = useState('running');
|
|
14
|
+
const [syncChanges, setSyncChanges] = useState([]);
|
|
15
|
+
const [syncDecision, setSyncDecision] = useState(false);
|
|
16
|
+
// Set when the user picks OAuth2 for an existing config, so the form opens in
|
|
17
|
+
// the OAuth2 branch without mutating the shared project object.
|
|
18
|
+
const [pendingAuthMethod, setPendingAuthMethod] = useState(undefined);
|
|
14
19
|
const appType = getAppType(project.manifest);
|
|
15
20
|
// Auto-advance through checks
|
|
16
21
|
useEffect(() => {
|
|
@@ -27,14 +32,36 @@ export function SetupFlow({ project, onComplete }) {
|
|
|
27
32
|
if (project.hasLegacyPassword) {
|
|
28
33
|
setStep('confirm-password-migration');
|
|
29
34
|
}
|
|
35
|
+
else if (project.devProperties?.authMethod === undefined) {
|
|
36
|
+
setStep('confirm-auth-method');
|
|
37
|
+
}
|
|
30
38
|
else {
|
|
31
|
-
setStep('check-
|
|
39
|
+
setStep('check-package-sync');
|
|
32
40
|
}
|
|
33
41
|
}
|
|
34
42
|
else {
|
|
35
43
|
setStep('confirm-dev-setup');
|
|
36
44
|
}
|
|
37
45
|
}
|
|
46
|
+
else if (step === 'check-package-sync') {
|
|
47
|
+
const preference = readSvcConfig(project.root).syncPackageJson;
|
|
48
|
+
const properties = project.devProperties;
|
|
49
|
+
const changes = preference !== false && properties
|
|
50
|
+
? getPackageJsonSyncChanges(project.root, properties)
|
|
51
|
+
: [];
|
|
52
|
+
if (changes.length === 0 || !properties) {
|
|
53
|
+
setStep('check-signing-properties');
|
|
54
|
+
}
|
|
55
|
+
else if (preference === true) {
|
|
56
|
+
syncDevPropertiesToPackageJson(project.root, properties);
|
|
57
|
+
onReload();
|
|
58
|
+
setStep('check-signing-properties');
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
setSyncChanges(changes);
|
|
62
|
+
setStep('confirm-package-sync');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
38
65
|
else if (step === 'check-signing-properties') {
|
|
39
66
|
if (project.hasSigningProperties) {
|
|
40
67
|
setStep('show-info');
|
|
@@ -84,6 +111,29 @@ export function SetupFlow({ project, onComplete }) {
|
|
|
84
111
|
else if (step === 'confirm-password-migration') {
|
|
85
112
|
if (input === 'y' || input === 'Y') {
|
|
86
113
|
migrateLegacyPassword(project);
|
|
114
|
+
// The file was rewritten (plaintext stripped, password moved to
|
|
115
|
+
// keychain) — re-detect so hasLegacyPassword/password reflect that.
|
|
116
|
+
onReload();
|
|
117
|
+
setStep('check-package-sync');
|
|
118
|
+
}
|
|
119
|
+
else if (input === 'n' || input === 'N') {
|
|
120
|
+
setStep('check-package-sync');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else if (step === 'confirm-package-sync') {
|
|
124
|
+
if (['y', 'Y', 'n', 'N'].includes(input)) {
|
|
125
|
+
const accepted = input.toLowerCase() === 'y';
|
|
126
|
+
if (accepted && project.devProperties) {
|
|
127
|
+
syncDevPropertiesToPackageJson(project.root, project.devProperties);
|
|
128
|
+
onReload();
|
|
129
|
+
}
|
|
130
|
+
setSyncDecision(accepted);
|
|
131
|
+
setStep('confirm-save-sync-choice');
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else if (step === 'confirm-save-sync-choice') {
|
|
135
|
+
if (input === 'y' || input === 'Y') {
|
|
136
|
+
writeSvcConfig(project.root, { syncPackageJson: syncDecision });
|
|
87
137
|
setStep('check-signing-properties');
|
|
88
138
|
}
|
|
89
139
|
else if (input === 'n' || input === 'N') {
|
|
@@ -98,21 +148,46 @@ export function SetupFlow({ project, onComplete }) {
|
|
|
98
148
|
setStep('show-info');
|
|
99
149
|
}
|
|
100
150
|
}
|
|
151
|
+
else if (step === 'confirm-auth-method') {
|
|
152
|
+
if (input === 'o' || input === 'O') {
|
|
153
|
+
// Choosing OAuth2 needs endpoints — collect them in the full form.
|
|
154
|
+
setPendingAuthMethod('oauth2');
|
|
155
|
+
setStep('setup-dev-properties');
|
|
156
|
+
}
|
|
157
|
+
else if (input === 'c' || input === 'C') {
|
|
158
|
+
setPendingAuthMethod('cookie');
|
|
159
|
+
setStep('setup-dev-properties');
|
|
160
|
+
}
|
|
161
|
+
else if (['b', 'B', '\r'].includes(input)) {
|
|
162
|
+
if (project.devProperties) {
|
|
163
|
+
writeDevProperties(project.root, {
|
|
164
|
+
...project.devProperties,
|
|
165
|
+
authMethod: 'basic',
|
|
166
|
+
});
|
|
167
|
+
onReload();
|
|
168
|
+
}
|
|
169
|
+
setStep('check-package-sync');
|
|
170
|
+
}
|
|
171
|
+
}
|
|
101
172
|
});
|
|
102
173
|
// Setup Dev Properties Form
|
|
103
174
|
if (step === 'setup-dev-properties') {
|
|
104
|
-
return (_jsx(DevPropertiesForm, { projectRoot: project.root, initialProperties:
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
//
|
|
108
|
-
|
|
109
|
-
|
|
175
|
+
return (_jsx(DevPropertiesForm, { projectRoot: project.root, initialProperties: pendingAuthMethod && project.devProperties
|
|
176
|
+
? { ...project.devProperties, authMethod: pendingAuthMethod }
|
|
177
|
+
: project.devProperties, packageJson: project.packageJson, onComplete: () => {
|
|
178
|
+
// Re-detect from disk/keychain so devProperties (incl. the keychain
|
|
179
|
+
// password) populate in memory — otherwise the rest of this flow and
|
|
180
|
+
// the menu would see stale state until the CLI is restarted.
|
|
181
|
+
onReload();
|
|
182
|
+
setStep('check-package-sync');
|
|
110
183
|
}, onCancel: () => setStep('check-signing-properties') }));
|
|
111
184
|
}
|
|
112
185
|
// Setup Signing Properties Form
|
|
113
186
|
if (step === 'setup-signing-properties') {
|
|
114
187
|
return (_jsx(SigningPropertiesForm, { projectRoot: project.root, onComplete: () => {
|
|
115
|
-
|
|
188
|
+
// Re-detect so signing credentials are reflected in memory before
|
|
189
|
+
// the info screen / menu render.
|
|
190
|
+
onReload();
|
|
116
191
|
setStep('show-info');
|
|
117
192
|
}, onCancel: () => setStep('show-info') }));
|
|
118
193
|
}
|
|
@@ -132,13 +207,27 @@ export function SetupFlow({ project, onComplete }) {
|
|
|
132
207
|
if (step === 'confirm-password-migration') {
|
|
133
208
|
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Sitevision CLI" }) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "yellow", children: "\u26A0 Plaintext password found in .dev_properties.json" }) }), _jsxs(Box, { marginBottom: 1, flexDirection: "column", children: [_jsx(Text, { children: "Move it to the OS keychain and remove it from the file? (y/n)" }), _jsx(Text, { dimColor: true, children: "Recommended \u2014 storing passwords in project files is insecure." })] })] }));
|
|
134
209
|
}
|
|
210
|
+
// Ask which auth method an existing config should use (setting is missing)
|
|
211
|
+
if (step === 'confirm-auth-method') {
|
|
212
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Sitevision CLI" }) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "yellow", children: "\u26A0 Deploy authentication method not set" }) }), _jsxs(Box, { marginBottom: 1, flexDirection: "column", children: [_jsx(Text, { children: "Which method should deploys use? [B]asic / [O]Auth2 / [C]ookie" }), _jsx(Text, { dimColor: true, children: "B = Basic (default), O = OAuth2 bearer, C = session cookie (SSO)." })] })] }));
|
|
213
|
+
}
|
|
214
|
+
// Confirm package.json sync
|
|
215
|
+
if (step === 'confirm-package-sync') {
|
|
216
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Sitevision CLI" }) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "yellow", children: "\u26A0 package.json is out of sync with .dev_properties.json" }) }), _jsx(Box, { marginBottom: 1, flexDirection: "column", marginLeft: 2, children: syncChanges.map(change => (_jsxs(Box, { children: [_jsx(Text, { color: change.from === undefined ? 'green' : 'yellow', children: change.from === undefined ? '+ ' : '~ ' }), _jsxs(Text, { bold: true, children: [change.key, ": "] }), change.from !== undefined && (_jsxs(Text, { dimColor: true, children: [change.from, " \u2192 "] })), _jsx(Text, { children: change.to })] }, change.key))) }), _jsxs(Box, { marginBottom: 1, flexDirection: "column", children: [_jsx(Text, { children: "Update package.json from .dev_properties.json? (y/n)" }), _jsx(Text, { dimColor: true, children: "sitevision-scripts reads these fields from package.json." })] })] }));
|
|
217
|
+
}
|
|
218
|
+
// Offer to persist the sync decision in .svcconfig
|
|
219
|
+
if (step === 'confirm-save-sync-choice') {
|
|
220
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Sitevision CLI" }) }), _jsxs(Box, { marginBottom: 1, flexDirection: "column", children: [_jsx(Text, { children: "Remember this choice in .svcconfig? (y/n)" }), _jsx(Text, { dimColor: true, children: syncDecision
|
|
221
|
+
? 'svc will update package.json automatically from now on.'
|
|
222
|
+
: 'svc will stop asking about package.json sync.' })] })] }));
|
|
223
|
+
}
|
|
135
224
|
// Confirm signing setup
|
|
136
225
|
if (step === 'confirm-signing-setup') {
|
|
137
226
|
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Sitevision CLI" }) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "yellow", children: "\u26A0 signing credentials not configured" }) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { children: "Signing credentials are required for signing apps on developer.sitevision.se" }) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { children: "Would you like to set up signing credentials? (y/n)" }) })] }));
|
|
138
227
|
}
|
|
139
228
|
// Show info
|
|
140
229
|
if (step === 'show-info') {
|
|
141
|
-
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Sitevision Project Information" }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Name: " }), _jsx(Text, { children: project.manifest.name })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "ID: " }), _jsx(Text, { children: project.manifest.id })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Version: " }), _jsx(Text, { children: project.manifest.version })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Type: " }), _jsx(Text, { color: "green", children: project.manifest.type }), _jsxs(Text, { dimColor: true, children: [" (", appType, ")"] })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Bundled: " }), _jsx(Text, { children: project.manifest.bundled ? 'Yes' : 'No' })] })] }), project.hasDevProperties && project.devProperties && (_jsxs(_Fragment, { children: [_jsx(Box, { marginTop: 1, marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Development Configuration" }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Domain: " }), _jsx(Text, { children: project.devProperties.domain })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Site: " }), _jsx(Text, { children: project.devProperties.siteName })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Addon: " }), _jsx(Text, { children: project.devProperties.addonName })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Username: " }), _jsx(Text, { children: project.devProperties.username })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Use HTTP: " }), _jsx(Text, { children: project.devProperties.useHTTPForDevDeploy ? 'Yes' : 'No' })] })] })] })), !project.hasDevProperties && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "yellow", children: "\u26A0 No dev properties configured" }) })), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { bold: true, children: "Project Root: " }), _jsx(Text, { dimColor: true, children: project.root })] })] }));
|
|
230
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Sitevision Project Information" }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Name: " }), _jsx(Text, { children: localizedText(project.manifest.name) })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "ID: " }), _jsx(Text, { children: project.manifest.id })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Version: " }), _jsx(Text, { children: project.manifest.version })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Type: " }), _jsx(Text, { color: "green", children: project.manifest.type }), _jsxs(Text, { dimColor: true, children: [" (", appType, ")"] })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Bundled: " }), _jsx(Text, { children: project.manifest.bundled ? 'Yes' : 'No' })] })] }), project.hasDevProperties && project.devProperties && (_jsxs(_Fragment, { children: [_jsx(Box, { marginTop: 1, marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: "Development Configuration" }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Domain: " }), _jsx(Text, { children: project.devProperties.domain })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Site: " }), _jsx(Text, { children: project.devProperties.siteName })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Addon: " }), _jsx(Text, { children: project.devProperties.addonName })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Username: " }), _jsx(Text, { children: project.devProperties.username })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Use HTTP: " }), _jsx(Text, { children: project.devProperties.useHTTPForDevDeploy ? 'Yes' : 'No' })] })] })] })), !project.hasDevProperties && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "yellow", children: "\u26A0 No dev properties configured" }) })), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { bold: true, children: "Project Root: " }), _jsx(Text, { dimColor: true, children: project.root })] })] }));
|
|
142
231
|
}
|
|
143
232
|
return null;
|
|
144
233
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -11,16 +11,22 @@ export type AppType = 'WebApp' | 'Widget' | 'RESTApp';
|
|
|
11
11
|
* Simplified app type for internal use
|
|
12
12
|
*/
|
|
13
13
|
export type SimpleAppType = 'web' | 'widget' | 'rest';
|
|
14
|
+
/**
|
|
15
|
+
* A manifest text field that may be a plain string or a localized object keyed
|
|
16
|
+
* by language code, e.g. `{sv: 'Namn', en: 'Name'}`. Sitevision allows either
|
|
17
|
+
* form for human-facing fields like `name` and `description`.
|
|
18
|
+
*/
|
|
19
|
+
export type LocalizedString = string | Record<string, string>;
|
|
14
20
|
/**
|
|
15
21
|
* Sitevision app manifest (manifest.json)
|
|
16
22
|
*/
|
|
17
23
|
export interface SitevisionManifest {
|
|
18
24
|
id: string;
|
|
19
|
-
name:
|
|
25
|
+
name: LocalizedString;
|
|
20
26
|
version: string;
|
|
21
27
|
type: AppType;
|
|
22
28
|
bundled?: boolean;
|
|
23
|
-
description?:
|
|
29
|
+
description?: LocalizedString;
|
|
24
30
|
author?: string;
|
|
25
31
|
helpUrl?: string;
|
|
26
32
|
license?: string;
|
|
@@ -42,6 +48,24 @@ export interface DevProperties {
|
|
|
42
48
|
useHTTPForDevDeploy?: boolean;
|
|
43
49
|
signingUsername?: string;
|
|
44
50
|
certificateName?: string;
|
|
51
|
+
authMethod?: 'basic' | 'oauth2' | 'cookie';
|
|
52
|
+
oauth2?: OAuth2Config;
|
|
53
|
+
sessionLoginUrl?: string;
|
|
54
|
+
accessToken?: string;
|
|
55
|
+
sessionCookie?: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* OAuth2 provider config for bearer-token deploys.
|
|
59
|
+
*
|
|
60
|
+
* Deliberately secret-free: the client secret and refresh token live in the OS
|
|
61
|
+
* keychain, so nothing here is unsafe to write to .dev_properties.json.
|
|
62
|
+
*/
|
|
63
|
+
export interface OAuth2Config {
|
|
64
|
+
authorizationEndpoint: string;
|
|
65
|
+
tokenEndpoint: string;
|
|
66
|
+
clientId: string;
|
|
67
|
+
scopes?: string[];
|
|
68
|
+
redirectPort?: number;
|
|
45
69
|
}
|
|
46
70
|
/**
|
|
47
71
|
* Signing credentials (password is runtime-only, not persisted)
|
|
@@ -59,7 +83,9 @@ export interface DeployConfig {
|
|
|
59
83
|
siteName: string;
|
|
60
84
|
addonName: string;
|
|
61
85
|
username: string;
|
|
62
|
-
password
|
|
86
|
+
password?: string;
|
|
87
|
+
accessToken?: string;
|
|
88
|
+
sessionCookie?: string;
|
|
63
89
|
useHTTP?: boolean;
|
|
64
90
|
}
|
|
65
91
|
/**
|
|
@@ -143,6 +169,7 @@ export interface DeployResponse {
|
|
|
143
169
|
executableId?: string;
|
|
144
170
|
message?: string;
|
|
145
171
|
error?: string;
|
|
172
|
+
authExpired?: boolean;
|
|
146
173
|
}
|
|
147
174
|
/**
|
|
148
175
|
* API response from addon creation
|
|
@@ -151,6 +178,7 @@ export interface CreateAddonResponse {
|
|
|
151
178
|
success: boolean;
|
|
152
179
|
addonId?: string;
|
|
153
180
|
error?: string;
|
|
181
|
+
authExpired?: boolean;
|
|
154
182
|
}
|
|
155
183
|
/**
|
|
156
184
|
* API response from activation
|
|
@@ -158,6 +186,7 @@ export interface CreateAddonResponse {
|
|
|
158
186
|
export interface ActivationResponse {
|
|
159
187
|
success: boolean;
|
|
160
188
|
error?: string;
|
|
189
|
+
authExpired?: boolean;
|
|
161
190
|
}
|
|
162
191
|
/**
|
|
163
192
|
* Build mode
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSONC helpers.
|
|
3
|
+
*
|
|
4
|
+
* Sitevision's own manifest documentation shows manifest.json with line and
|
|
5
|
+
* block comments (e.g. `"name": { // Multilingual-manifest requires SV 10.1`),
|
|
6
|
+
* so real-world manifests copied from the docs contain them. Strict JSON.parse
|
|
7
|
+
* rejects those, so we strip comments before parsing.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Remove line comments (//...) and block comments from a JSON string, leaving
|
|
11
|
+
* everything inside string literals untouched (so values like
|
|
12
|
+
* `"https://example.com"` survive).
|
|
13
|
+
*/
|
|
14
|
+
export declare function stripJsonComments(input: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Parse a JSON string that may contain comments (JSONC). Throws the underlying
|
|
17
|
+
* SyntaxError if the content is invalid even after comments are removed.
|
|
18
|
+
*/
|
|
19
|
+
export declare function parseJsonc<T>(input: string): T;
|