sitevision-cli 0.6.0-beta.2 → 1.0.0-beta.1

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.js CHANGED
@@ -24,6 +24,15 @@ export default function App({ project }) {
24
24
  // slow and this component re-renders frequently).
25
25
  const signingUsername = project.devProperties?.signingUsername;
26
26
  const storedSigningPassword = useMemo(() => (signingUsername ? getSigningPassword(signingUsername) : null), [signingUsername]);
27
+ // Map a signing-capable command to the screen it lands on once the signing
28
+ // password is resolved.
29
+ const signedDestination = (command = currentCommand) => {
30
+ if (command === 'dev-signed')
31
+ return 'dev';
32
+ if (command === 'watch-signed')
33
+ return 'watch';
34
+ return 'sign';
35
+ };
27
36
  // Decide the next step once a signing password is needed: proceed if we already
28
37
  // have one this session, offer the keychain choice if one is saved, otherwise
29
38
  // prompt for manual entry.
@@ -34,7 +43,7 @@ export default function App({ project }) {
34
43
  isRetry: signingRetry,
35
44
  });
36
45
  if (step === 'proceed') {
37
- setState(command === 'dev-signed' ? 'dev' : 'sign');
46
+ setState(signedDestination(command));
38
47
  }
39
48
  else if (step === 'choice') {
40
49
  setState('signing-password-choice');
@@ -78,7 +87,7 @@ export default function App({ project }) {
78
87
  if (storedSigningPassword) {
79
88
  setSigningPassword(storedSigningPassword);
80
89
  }
81
- setState(currentCommand === 'dev-signed' ? 'dev' : 'sign');
90
+ setState(signedDestination());
82
91
  };
83
92
  const handleEnterNewSigning = () => {
84
93
  setState('signing-password-input');
@@ -88,12 +97,7 @@ export default function App({ project }) {
88
97
  if (remember && project.devProperties?.signingUsername && password) {
89
98
  saveSigningPassword(project.devProperties.signingUsername, password);
90
99
  }
91
- if (currentCommand === 'dev-signed') {
92
- setState('dev');
93
- }
94
- else {
95
- setState('sign');
96
- }
100
+ setState(signedDestination());
97
101
  };
98
102
  const handleCommandSelect = (command) => {
99
103
  setCurrentCommand(command);
@@ -135,6 +139,21 @@ export default function App({ project }) {
135
139
  routeToSigningStep(command);
136
140
  }
137
141
  break;
142
+ case 'watch':
143
+ // Build/sign-only: no deploy and no signing, so no credentials needed.
144
+ setState('watch');
145
+ break;
146
+ case 'watch-signed':
147
+ if (!project.hasDevProperties || !project.devProperties) {
148
+ console.log('\x1b[31mDevelopment properties not configured. Create a .dev_properties.json file first.\x1b[0m');
149
+ return;
150
+ }
151
+ if (!project.hasSigningProperties) {
152
+ console.log('\x1b[31mSigning credentials not configured. Run svc setup-signing first.\x1b[0m');
153
+ return;
154
+ }
155
+ routeToSigningStep(command);
156
+ break;
138
157
  case 'sign':
139
158
  if (!project.hasDevProperties || !project.devProperties) {
140
159
  console.log('\x1b[31mDevelopment properties not configured. Create a .dev_properties.json file first.\x1b[0m');
@@ -205,6 +224,23 @@ export default function App({ project }) {
205
224
  }
206
225
  : undefined }));
207
226
  }
227
+ if (state === 'watch') {
228
+ const watchSigned = currentCommand === 'watch-signed';
229
+ return (_jsx(DevScreen, { projectRoot: project.root, manifest: project.manifest, devProperties: project.devProperties, signed: watchSigned, deploy: false, onBack: () => setState('menu'), onRetryCredentials: watchSigned
230
+ ? () => {
231
+ setSigningPassword('');
232
+ // The saved password may be what failed — don't re-offer it.
233
+ setSigningRetry(true);
234
+ routeToSigningStep('watch-signed');
235
+ }
236
+ : undefined, signingCredentials: watchSigned && project.devProperties?.signingUsername
237
+ ? {
238
+ username: project.devProperties.signingUsername,
239
+ password: signingPassword,
240
+ certificateName: project.devProperties.certificateName,
241
+ }
242
+ : undefined }));
243
+ }
208
244
  if (state === 'build') {
209
245
  return (_jsx(BuildScreen, { projectRoot: project.root, manifest: project.manifest, createZip: true, onBack: () => setState('menu') }));
210
246
  }
package/dist/cli.js CHANGED
@@ -21,7 +21,9 @@ const cli = meow(`
21
21
 
22
22
  Commands
23
23
  dev Start development server with watch mode
24
+ watch Watch and rebuild (optionally signing) without deploying
24
25
  build Build the application for production
26
+ sign Sign the app for production deployment
25
27
  deploy Deploy the application
26
28
  info Show project information
27
29
 
@@ -33,6 +35,8 @@ const cli = meow(`
33
35
  $ svc # Interactive menu
34
36
  $ svc dev
35
37
  $ svc dev --signed
38
+ $ svc watch
39
+ $ svc watch --signed
36
40
  $ svc build
37
41
  $ svc deploy --force
38
42
  $ svc deploy --production
@@ -4,12 +4,13 @@ import type { SitevisionManifest, DevProperties, SigningCredentials } from '../t
4
4
  interface DevScreenProps {
5
5
  projectRoot: string;
6
6
  manifest: SitevisionManifest;
7
- devProperties: DevProperties;
7
+ devProperties?: DevProperties;
8
8
  signed: boolean;
9
+ deploy?: boolean;
9
10
  signingCredentials?: SigningCredentials;
10
11
  onBack?: () => void;
11
12
  onRetryCredentials?: () => void;
12
13
  }
13
- export declare function DevScreen({ projectRoot, manifest, devProperties, signed, signingCredentials, onBack, onRetryCredentials, }: DevScreenProps): React.JSX.Element;
14
+ export declare function DevScreen({ projectRoot, manifest, devProperties, signed, deploy, signingCredentials, onBack, onRetryCredentials, }: DevScreenProps): React.JSX.Element;
14
15
  export declare const devCommand: Command;
15
16
  export {};
@@ -12,7 +12,7 @@ import { setDeployPassword } from '../utils/keychain.js';
12
12
  import { resolveSigningPassword } from '../utils/signing-password.js';
13
13
  import { copyStaticToBuild, createBuildZip, cleanBuild } from '../utils/zip.js';
14
14
  import { isBundledApp, getAppType, getFullAppId, getZipPath, getSignedZipPath, } from '../utils/project-detection.js';
15
- export function DevScreen({ projectRoot, manifest, devProperties, signed, signingCredentials, onBack, onRetryCredentials, }) {
15
+ export function DevScreen({ projectRoot, manifest, devProperties, signed, deploy = true, signingCredentials, onBack, onRetryCredentials, }) {
16
16
  const { exit } = useApp();
17
17
  const [state, setState] = React.useState({
18
18
  status: 'initializing',
@@ -57,6 +57,20 @@ export function DevScreen({ projectRoot, manifest, devProperties, signed, signin
57
57
  }
58
58
  deployZipPath = signedZipPath;
59
59
  }
60
+ // Watch/build-only mode: stop after building (and signing).
61
+ if (!deploy || !devProperties) {
62
+ setState(prev => ({
63
+ ...prev,
64
+ status: 'ready',
65
+ message: signed
66
+ ? 'Signed. Watching for changes...'
67
+ : 'Built. Watching for changes...',
68
+ buildCount: prev.buildCount + 1,
69
+ lastBuildTime: buildTime,
70
+ error: undefined,
71
+ }));
72
+ return;
73
+ }
60
74
  // Deploy
61
75
  setState(prev => ({
62
76
  ...prev,
@@ -99,7 +113,7 @@ export function DevScreen({ projectRoot, manifest, devProperties, signed, signin
99
113
  error: error instanceof Error ? error.message : String(error),
100
114
  }));
101
115
  }
102
- }, [projectRoot, manifest, devProperties, signed, signingCredentials]);
116
+ }, [projectRoot, manifest, devProperties, signed, deploy, signingCredentials]);
103
117
  // In-house webpack path: copy static, zip, then sign + deploy.
104
118
  const handleBuildComplete = React.useCallback(async (result) => {
105
119
  if (!result.success) {
@@ -339,7 +353,7 @@ export function DevScreen({ projectRoot, manifest, devProperties, signed, signin
339
353
  };
340
354
  return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(StatusIndicator, { status: getStatusType(), label: getStatusLabel(), message: state.message }) }), state.warning && (_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: "yellow", children: ["\u26A0 ", state.warning] }) })), state.buildCount > 0 && (_jsx(Box, { marginLeft: 2, marginBottom: 1, children: _jsxs(Text, { dimColor: true, children: ["Builds: ", state.buildCount, state.lastBuildTime
341
355
  ? ` | Last build: ${state.lastBuildTime}ms`
342
- : '', signed ? ' | Signed mode' : ''] }) })), _jsx(Box, { marginLeft: 2, marginBottom: 1, children: _jsxs(Text, { dimColor: true, children: [manifest.name, " v", manifest.version] }) }), devProperties && (_jsx(Box, { marginLeft: 2, marginBottom: 1, children: _jsxs(Text, { dimColor: true, children: ["Target: ", devProperties.domain, "/", devProperties.siteName, "/", devProperties.addonName] }) })), state.status === 'error' && state.error && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(Text, { color: "red", children: state.error }) })), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [state.status === 'error' && onRetryCredentials && (_jsx(Text, { dimColor: true, children: "Press r to retry with new credentials" })), onBack ? (_jsx(Text, { dimColor: true, children: "Press q or Esc to return to menu (Ctrl+C to stop process)" })) : (_jsx(Text, { dimColor: true, children: "Press Ctrl+C to stop" }))] })] }));
356
+ : '', signed ? ' | Signed mode' : ''] }) })), _jsx(Box, { marginLeft: 2, marginBottom: 1, children: _jsxs(Text, { dimColor: true, children: [manifest.name, " v", manifest.version] }) }), deploy && devProperties && (_jsx(Box, { marginLeft: 2, marginBottom: 1, children: _jsxs(Text, { dimColor: true, children: ["Target: ", devProperties.domain, "/", devProperties.siteName, "/", devProperties.addonName] }) })), state.status === 'error' && state.error && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(Text, { color: "red", children: state.error }) })), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [state.status === 'error' && onRetryCredentials && (_jsx(Text, { dimColor: true, children: "Press r to retry with new credentials" })), onBack ? (_jsx(Text, { dimColor: true, children: "Press q or Esc to return to menu (Ctrl+C to stop process)" })) : (_jsx(Text, { dimColor: true, children: "Press Ctrl+C to stop" }))] })] }));
343
357
  }
344
358
  export const devCommand = {
345
359
  name: 'dev',
@@ -1,4 +1,5 @@
1
1
  import { devCommand } from './dev.js';
2
+ import { watchCommand } from './watch.js';
2
3
  import { buildCommand } from './build.js';
3
4
  import { deployCommand } from './deploy.js';
4
5
  import { infoCommand } from './info.js';
@@ -6,6 +7,7 @@ import { setupSigningCommand } from './setup-signing.js';
6
7
  import { signCommand } from './sign.js';
7
8
  export const commands = [
8
9
  devCommand,
10
+ watchCommand,
9
11
  buildCommand,
10
12
  signCommand,
11
13
  deployCommand,
@@ -0,0 +1,2 @@
1
+ import { type Command } from './types.js';
2
+ export declare const watchCommand: Command;
@@ -0,0 +1,44 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { render } from 'ink';
3
+ import { DevScreen } from './dev.js';
4
+ import { resolveSigningPassword } from '../utils/signing-password.js';
5
+ export const watchCommand = {
6
+ name: 'watch',
7
+ description: 'Watch and rebuild (optionally signing) without deploying',
8
+ requiresProject: true,
9
+ flags: {
10
+ signed: {
11
+ type: 'boolean',
12
+ description: 'Sign after each build',
13
+ alias: 's',
14
+ default: false,
15
+ },
16
+ },
17
+ async execute({ project, flags }) {
18
+ const signed = Boolean(flags['signed']);
19
+ let signingCredentials;
20
+ // Signed mode: resolve signing credentials (keychain → env → prompt).
21
+ // No deploy credentials are needed — watch never deploys.
22
+ if (signed) {
23
+ if (!project.hasSigningProperties ||
24
+ !project.devProperties?.signingUsername) {
25
+ console.log('\n\x1b[33mSigning credentials not configured.\x1b[0m');
26
+ console.log('Run \x1b[36msetup-signing\x1b[0m to configure credentials.\n');
27
+ return;
28
+ }
29
+ const signingUsername = project.devProperties.signingUsername;
30
+ const password = await resolveSigningPassword(signingUsername);
31
+ if (!password) {
32
+ console.log('\x1b[31mError: Password is required for signed mode\x1b[0m');
33
+ return;
34
+ }
35
+ signingCredentials = {
36
+ username: signingUsername,
37
+ password,
38
+ certificateName: project.devProperties.certificateName,
39
+ };
40
+ }
41
+ const { waitUntilExit } = render(_jsx(DevScreen, { projectRoot: project.root, manifest: project.manifest, devProperties: project.devProperties, signed: signed, deploy: false, signingCredentials: signingCredentials }));
42
+ await waitUntilExit();
43
+ },
44
+ };
@@ -56,7 +56,7 @@ export function DevPropertiesForm({ projectRoot, initialProperties, packageJson,
56
56
  case 'password':
57
57
  return (_jsx(TextInput, { label: "Password (saved in OS keychain \u2014 leave empty to prompt on each run)", type: "password", defaultValue: properties.password, onSubmit: (value) => handleNext('password', value), onCancel: onCancel }, "password"));
58
58
  case 'useHTTP':
59
- return (_jsx(BooleanInput, { label: "Use HTTP for deployment? (y/n)", defaultValue: properties.useHTTPForDevDeploy, onSubmit: (value) => handleNext('useHTTPForDevDeploy', value) }, "useHTTP"));
59
+ return (_jsx(BooleanInput, { label: "Use HTTP for deployment? (y/n)", defaultValue: properties.useHTTPForDevDeploy ?? false, onSubmit: (value) => handleNext('useHTTPForDevDeploy', value) }, "useHTTP"));
60
60
  default:
61
61
  return null;
62
62
  }
@@ -16,6 +16,16 @@ export function MainMenu({ project, onSelect }) {
16
16
  value: 'dev-signed',
17
17
  description: 'Development with automatic signing',
18
18
  },
19
+ {
20
+ label: '👀 Watch',
21
+ value: 'watch',
22
+ description: 'Rebuild on change without deploying',
23
+ },
24
+ {
25
+ label: '👀 Watch (Signed)',
26
+ value: 'watch-signed',
27
+ description: 'Rebuild and sign on change without deploying',
28
+ },
19
29
  {
20
30
  label: '🔨 Build',
21
31
  value: 'build',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sitevision-cli",
3
- "version": "0.6.0-beta.2",
3
+ "version": "1.0.0-beta.1",
4
4
  "license": "MIT",
5
5
  "bin": {
6
6
  "svc": "dist/cli.js"