@selvajs/cli 4.6.21-beta.1 → 4.7.0-beta.3

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Selva FelixBrunold VektorNode
3
+ Copyright (c) 2025 Selva VektorNode AG
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/package.json CHANGED
@@ -1,11 +1,16 @@
1
1
  {
2
2
  "name": "@selvajs/cli",
3
- "version": "4.6.21-beta.1",
3
+ "version": "4.7.0-beta.3",
4
4
  "description": "Scaffold and operate a Selva white-label deployment. `npx @selvajs/cli <dir>` to bootstrap, `selva <cmd>` to manage.",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/VektorNode/selva.git",
12
+ "directory": "packages/cli"
13
+ },
9
14
  "type": "module",
10
15
  "bin": {
11
16
  "cli": "./bin/cli.js",
@@ -16,7 +21,7 @@
16
21
  "src"
17
22
  ],
18
23
  "engines": {
19
- "node": ">=20.6.0"
24
+ "node": ">=22.0.0"
20
25
  },
21
26
  "dependencies": {
22
27
  "@clack/prompts": "^0.11.0",
package/src/cli.js CHANGED
@@ -35,9 +35,6 @@ export async function runSelva(argv) {
35
35
  }
36
36
 
37
37
  if (command === '--version' || command === '-v') {
38
- // We don't pin a version here — selva is installed transitively, so
39
- // the deployment's package.json doesn't list us directly. Read our
40
- // own package.json instead.
41
38
  const { readFileSync } = await import('node:fs');
42
39
  const { fileURLToPath } = await import('node:url');
43
40
  const { dirname, join } = await import('node:path');
@@ -58,7 +58,7 @@ function runPm2(dir, args, { inherit = true } = {}) {
58
58
  const result = spawnSync(bin, args, {
59
59
  cwd: dir,
60
60
  stdio: inherit ? 'inherit' : 'pipe',
61
- shell: process.platform === 'win32' // allow .cmd shim resolution
61
+ shell: process.platform === 'win32'
62
62
  });
63
63
  if (result.error) {
64
64
  throw new Error(
@@ -125,10 +125,8 @@ export async function runUpdate() {
125
125
  return;
126
126
  }
127
127
 
128
- // Resync daemon before state changes (avoid half-state stop/start).
129
128
  ensurePm2InSync(dir);
130
129
 
131
- // Stop before npm rewrites (SvelteKit lazy-loads chunks; see migrate command).
132
130
  const stopStatus = runPm2(dir, ['stop', APP_NAME], { inherit: false });
133
131
  if (stopStatus !== 0) {
134
132
  p.log.warn('pm2 stop did not succeed — selva-compute may not be running. Continuing.');
@@ -137,7 +135,6 @@ export async function runUpdate() {
137
135
  const s = p.spinner();
138
136
  s.start(`npm update ${packages.join(' ')}`);
139
137
  try {
140
- // --prefer-online bypasses npm's packument cache (see docs/Hotfix-CLI-Runtime.md).
141
138
  execSync(`npm update --save --prefer-online ${packages.join(' ')}`, {
142
139
  cwd: dir,
143
140
  stdio: 'pipe'
@@ -145,7 +142,6 @@ export async function runUpdate() {
145
142
  s.stop('npm update finished');
146
143
  } catch (err) {
147
144
  s.stop('npm update failed');
148
- // Best-effort: bring old process back up.
149
145
  runPm2(dir, ['start', APP_NAME, '--update-env'], { inherit: false });
150
146
  throw err;
151
147
  }
@@ -153,7 +149,6 @@ export async function runUpdate() {
153
149
  const after = readRuntimeVersion(dir);
154
150
  p.log.info(`New @selvajs/selva: ${after ?? 'unknown'}`);
155
151
 
156
- // Surface no-op updates (cache may be stale; propagation delay is real).
157
152
  if (before && after && before === after) {
158
153
  p.log.warn(
159
154
  [
@@ -168,7 +163,6 @@ export async function runUpdate() {
168
163
  );
169
164
  }
170
165
 
171
- // Start the new build under PM2.
172
166
  const status = runPm2(dir, ['start', APP_NAME, '--update-env'], { inherit: false });
173
167
  if (status === 0) {
174
168
  p.outro(pc.green('Started ' + APP_NAME));
package/src/prompts.js CHANGED
@@ -10,8 +10,6 @@ function envBool(v) {
10
10
  return TRUTHY.has(String(v).toLowerCase());
11
11
  }
12
12
 
13
- // Non-interactive sibling of collectConfig: read from env, validate strictly,
14
- // fail loud for security-relevant fields (no safe defaults).
15
13
  export function collectConfigFromEnv(env = process.env) {
16
14
  const tenancy = pick(env.SELVA_TENANCY, ['single', 'multi'], 'single', 'SELVA_TENANCY');
17
15
  const auth = pick(
@@ -61,8 +59,6 @@ export function collectConfigFromEnv(env = process.env) {
61
59
  }
62
60
 
63
61
  if (auth === 'header') {
64
- // Same loopback default as the prompt — header-auth without network
65
- // isolation is the documented worst-case footgun.
66
62
  values.HOST = env.HOST || '127.0.0.1';
67
63
  if (data !== 'local') {
68
64
  values.HEADER_AUTH_DATA_DIR = requireEnv(env, 'HEADER_AUTH_DATA_DIR');
@@ -102,7 +98,6 @@ export function collectConfigFromEnv(env = process.env) {
102
98
  }
103
99
  values.ORIGIN = origin;
104
100
 
105
- // Pass-through: SELVA_FLAG_* vars written verbatim; unset = empty string.
106
101
  const flagNames = [
107
102
  'ALLOW_ORG_CREATION',
108
103
  'ALLOW_CROSS_ORG_PUBLIC',
@@ -140,9 +135,6 @@ export async function collectConfig({ defaults = {}, mode = 'create' } = {}) {
140
135
  p.intro(pc.bgCyan(pc.black(isInit ? ' selva init ' : ' Selva — new deployment ')));
141
136
 
142
137
  // Brand prompts (SELVA_BRAND_NAME / COPYRIGHT_NAME / TAGLINE / DESCRIPTION)
143
- // are skipped for now — the runtime falls back to "Selva" defaults when
144
- // these env vars are absent. To re-enable, add a brand prompt block here
145
- // and write the values into `values` below.
146
138
 
147
139
  const tenancy = await p.select({
148
140
  message: 'Tenancy mode',
@@ -300,7 +292,6 @@ export async function collectConfig({ defaults = {}, mode = 'create' } = {}) {
300
292
  cancelOn(dataDir);
301
293
  providerValues.HEADER_AUTH_DATA_DIR = String(dataDir);
302
294
  } else if (defaults.HEADER_AUTH_DATA_DIR) {
303
- // Preserve an explicit override if the operator set one previously.
304
295
  providerValues.HEADER_AUTH_DATA_DIR = defaults.HEADER_AUTH_DATA_DIR;
305
296
  }
306
297
 
@@ -397,10 +388,6 @@ export async function collectConfig({ defaults = {}, mode = 'create' } = {}) {
397
388
  initialValue: defaults.BOOTSTRAP_INSTANCE_ADMIN_EMAIL ?? '',
398
389
  validate: (v) => {
399
390
  if (!v) {
400
- // Blank is allowed in single-tenant non-header. Header-auth and
401
- // multi-tenant both need the email — without it there is no
402
- // supported path to first-admin (header-auth) or the first
403
- // random signup gets staff perms (multi-tenant).
404
391
  return adminRequired ? 'Required for this configuration.' : undefined;
405
392
  }
406
393
  if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v)) return 'Not a valid email.';
@@ -435,7 +422,6 @@ export async function collectConfig({ defaults = {}, mode = 'create' } = {}) {
435
422
  cancelOn(value);
436
423
  origin = String(value);
437
424
 
438
- // Plain HTTP kills Secure cookies in production (login appears to succeed, then anon).
439
425
  if (origin.startsWith('http://')) {
440
426
  p.note(
441
427
  'Sessions use Secure cookies in production; browsers will silently\n' +
@@ -528,7 +514,6 @@ function stringValue(v) {
528
514
  return String(v);
529
515
  }
530
516
 
531
- // Header-auth security: deployment IS the boundary (runtime enforcement impossible).
532
517
  function headerAuthSecurityWarning() {
533
518
  return [
534
519
  'Header-auth trusts identity headers from the upstream proxy. Anyone who',