sitevision-cli 1.0.0-beta.13 → 1.0.0-beta.15

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.
Files changed (43) hide show
  1. package/dist/cli.js +70 -41
  2. package/dist/commands/build.js +1 -1
  3. package/dist/commands/dev.d.ts +8 -10
  4. package/dist/commands/dev.js +75 -390
  5. package/dist/commands/watch.js +5 -23
  6. package/dist/components/AuthLoginScreen.js +2 -1
  7. package/dist/components/DevPropertiesForm.js +6 -3
  8. package/dist/components/PasswordInput.js +2 -1
  9. package/dist/shell/AddonPicker.d.ts +14 -0
  10. package/dist/shell/AddonPicker.js +54 -0
  11. package/dist/shell/CommandPalette.d.ts +8 -0
  12. package/dist/shell/CommandPalette.js +63 -0
  13. package/dist/shell/ConfigForm.d.ts +35 -0
  14. package/dist/shell/ConfigForm.js +472 -0
  15. package/dist/shell/Frame.d.ts +52 -0
  16. package/dist/shell/Frame.js +98 -0
  17. package/dist/shell/Settings.d.ts +6 -0
  18. package/dist/shell/Settings.js +96 -0
  19. package/dist/shell/Shell.d.ts +8 -0
  20. package/dist/shell/Shell.js +520 -0
  21. package/dist/shell/Tabs.d.ts +36 -0
  22. package/dist/shell/Tabs.js +85 -0
  23. package/dist/shell/actions.d.ts +45 -0
  24. package/dist/shell/actions.js +0 -0
  25. package/dist/types/index.d.ts +12 -2
  26. package/dist/utils/config.d.ts +10 -0
  27. package/dist/utils/config.js +14 -0
  28. package/dist/utils/environments.d.ts +20 -0
  29. package/dist/utils/environments.js +74 -0
  30. package/dist/utils/i18n.d.ts +12 -0
  31. package/dist/utils/i18n.js +263 -0
  32. package/dist/utils/oauth2-auth.d.ts +1 -0
  33. package/dist/utils/oauth2-auth.js +9 -12
  34. package/dist/utils/project-detection.d.ts +23 -1
  35. package/dist/utils/project-detection.js +124 -51
  36. package/dist/utils/sitevision-api.d.ts +35 -0
  37. package/dist/utils/sitevision-api.js +74 -1
  38. package/dist/utils/tasks.d.ts +48 -0
  39. package/dist/utils/tasks.js +371 -0
  40. package/dist/utils/workspace.d.ts +8 -0
  41. package/dist/utils/workspace.js +48 -0
  42. package/package.json +2 -1
  43. package/readme.md +76 -24
@@ -0,0 +1,45 @@
1
+ import type { ProjectInfo, DeployConfig } from '../types/index.js';
2
+ export type Tab = 'overview' | 'config' | 'versions' | 'log';
3
+ export type Credential = {
4
+ accessToken?: string;
5
+ sessionCookie?: string;
6
+ };
7
+ export interface ActionContext {
8
+ project: ProjectInfo;
9
+ reload: () => void;
10
+ askPassword: (label: string, rememberLabel?: string) => Promise<{
11
+ password: string;
12
+ remember: boolean;
13
+ } | null>;
14
+ login: (method: 'oauth2' | 'cookie') => Promise<Credential | null>;
15
+ confirm: (message: string) => Promise<boolean>;
16
+ setTab: (tab: Tab) => void;
17
+ openWorkspaceSettings?: () => void;
18
+ openSettings: () => void;
19
+ environment: string;
20
+ isProduction: boolean;
21
+ cycleEnvironment: () => void;
22
+ addEnvironment: () => Promise<void>;
23
+ notify: (text: string, level?: 'info' | 'ok' | 'warn' | 'error') => void;
24
+ quit: () => void;
25
+ }
26
+ export interface Action {
27
+ id: string;
28
+ key?: string;
29
+ group: 'app' | 'setup' | 'auth';
30
+ label: string;
31
+ detail?: (project: ProjectInfo) => string | undefined;
32
+ enabled?: (project: ProjectInfo) => boolean;
33
+ run: (ctx: ActionContext) => Promise<void>;
34
+ }
35
+ export declare function forgetSession(project: ProjectInfo): void;
36
+ /** Top-bar auth state without prompting for anything. */
37
+ export declare function authState(project: ProjectInfo): {
38
+ ready: boolean;
39
+ label: string;
40
+ };
41
+ export declare function resolveDeployConfig(ctx: ActionContext, fresh?: boolean): Promise<DeployConfig | null>;
42
+ export declare const actions: Action[];
43
+ export declare function actionForKey(key: string): Action | undefined;
44
+ /** Subsequence match, case-insensitive: "dpp" matches "Deploy to production". */
45
+ export declare function fuzzyMatch(query: string, text: string): boolean;
Binary file
@@ -6,11 +6,11 @@
6
6
  /**
7
7
  * App types supported by Sitevision
8
8
  */
9
- export type AppType = 'WebApp' | 'Widget' | 'RESTApp';
9
+ export type AppType = 'WebApp' | 'Widget' | 'RESTApp' | 'MCPServer';
10
10
  /**
11
11
  * Simplified app type for internal use
12
12
  */
13
- export type SimpleAppType = 'web' | 'widget' | 'rest';
13
+ export type SimpleAppType = 'web' | 'widget' | 'rest' | 'mcp';
14
14
  /**
15
15
  * A manifest text field that may be a plain string or a localized object keyed
16
16
  * by language code, e.g. `{sv: 'Namn', en: 'Name'}`. Sitevision allows either
@@ -51,9 +51,18 @@ export interface DevProperties {
51
51
  authMethod?: 'basic' | 'oauth2' | 'cookie';
52
52
  oauth2?: OAuth2Config;
53
53
  sessionLoginUrl?: string;
54
+ baseEnvironment?: string;
55
+ production?: boolean;
56
+ environments?: Record<string, EnvironmentOverride>;
54
57
  accessToken?: string;
55
58
  sessionCookie?: string;
59
+ environmentName?: string;
60
+ productionEnvironment?: boolean;
56
61
  }
62
+ /** Fields an environment may override; everything else comes from the base. */
63
+ export type EnvironmentOverride = Partial<Pick<DevProperties, 'domain' | 'siteName' | 'addonName' | 'username' | 'authMethod' | 'oauth2' | 'sessionLoginUrl' | 'useHTTPForDevDeploy'>> & {
64
+ production?: boolean;
65
+ };
57
66
  /**
58
67
  * OAuth2 provider config for bearer-token deploys.
59
68
  *
@@ -112,6 +121,7 @@ export interface ProjectInfo {
112
121
  hasSigningProperties: boolean;
113
122
  hasLegacyPassword: boolean;
114
123
  devProperties?: DevProperties;
124
+ inheritedKeys: string[];
115
125
  packageJson: PackageJson;
116
126
  hasSitevisionScripts: boolean;
117
127
  hasNodeModules: boolean;
@@ -1,3 +1,13 @@
1
+ import { type Language } from './i18n.js';
2
+ /** User-facing preferences editable from the shell's Settings screen. */
3
+ export interface Settings {
4
+ language: Language;
5
+ introAnimation: boolean;
6
+ }
7
+ export declare function getSettings(): Settings;
8
+ export declare function setSettings(patch: Partial<Settings>): void;
9
+ /** Path of the config file, for display. */
10
+ export declare function settingsFile(): string;
1
11
  /**
2
12
  * True until the user has completed the first-run welcome at least once.
3
13
  */
@@ -26,6 +26,20 @@ function writeConfig(config) {
26
26
  // shows again next time, which is harmless.
27
27
  }
28
28
  }
29
+ export function getSettings() {
30
+ const config = readConfig();
31
+ return {
32
+ language: config.language ?? 'en',
33
+ introAnimation: config.introAnimation ?? true,
34
+ };
35
+ }
36
+ export function setSettings(patch) {
37
+ writeConfig({ ...readConfig(), ...patch });
38
+ }
39
+ /** Path of the config file, for display. */
40
+ export function settingsFile() {
41
+ return configFile();
42
+ }
29
43
  /**
30
44
  * True until the user has completed the first-run welcome at least once.
31
45
  */
@@ -0,0 +1,20 @@
1
+ import type { DevProperties, EnvironmentOverride, ProjectInfo } from '../types/index.js';
2
+ /** Name of the environment the top-level dev properties describe. */
3
+ export declare function baseEnvironment(dev?: Partial<DevProperties>): string;
4
+ export declare function environmentNames(dev?: Partial<DevProperties>): string[];
5
+ export declare function isProductionEnvironment(name: string, dev?: Partial<DevProperties>): boolean;
6
+ /** Badge colour: dev green, production red, anything else yellow. */
7
+ export declare function environmentColor(name: string, dev?: Partial<DevProperties>): 'green' | 'yellow' | 'red';
8
+ /**
9
+ * The effective dev properties for one environment: the base with that
10
+ * environment's overrides applied and credentials resolved for its
11
+ * domain/username. The base ("dev") is returned as-is.
12
+ */
13
+ export declare function resolveEnvironment(dev: DevProperties, name: string): DevProperties;
14
+ /** A project view whose dev properties are resolved for `name`. */
15
+ export declare function environmentProject(project: ProjectInfo, name: string): ProjectInfo;
16
+ /**
17
+ * Write one environment's override into the base: keep only the keys whose
18
+ * value differs from the base, so the file stays minimal.
19
+ */
20
+ export declare function withEnvironmentOverride(base: DevProperties, name: string, values: EnvironmentOverride): DevProperties;
@@ -0,0 +1,74 @@
1
+ import { resolveRuntimeSecrets } from './project-detection.js';
2
+ /** Name of the environment the top-level dev properties describe. */
3
+ export function baseEnvironment(dev) {
4
+ return dev?.baseEnvironment?.trim() || 'dev';
5
+ }
6
+ export function environmentNames(dev) {
7
+ const base = baseEnvironment(dev);
8
+ return [
9
+ base,
10
+ ...Object.keys(dev?.environments ?? {}).filter(name => name !== base),
11
+ ];
12
+ }
13
+ export function isProductionEnvironment(name, dev) {
14
+ if (name === baseEnvironment(dev))
15
+ return dev?.production ?? false;
16
+ return dev?.environments?.[name]?.production ?? /prod/i.test(name);
17
+ }
18
+ /** Badge colour: dev green, production red, anything else yellow. */
19
+ export function environmentColor(name, dev) {
20
+ if (isProductionEnvironment(name, dev))
21
+ return 'red';
22
+ return name === baseEnvironment(dev) ? 'green' : 'yellow';
23
+ }
24
+ /**
25
+ * The effective dev properties for one environment: the base with that
26
+ * environment's overrides applied and credentials resolved for its
27
+ * domain/username. The base ("dev") is returned as-is.
28
+ */
29
+ export function resolveEnvironment(dev, name) {
30
+ const production = isProductionEnvironment(name, dev);
31
+ if (name === baseEnvironment(dev)) {
32
+ return { ...dev, environmentName: name, productionEnvironment: production };
33
+ }
34
+ const override = dev.environments?.[name] ?? {};
35
+ const { production: _production, ...fields } = override;
36
+ const resolved = {
37
+ ...dev,
38
+ ...fields,
39
+ password: undefined,
40
+ accessToken: undefined,
41
+ sessionCookie: undefined,
42
+ environmentName: name,
43
+ productionEnvironment: production,
44
+ };
45
+ return resolveRuntimeSecrets(resolved);
46
+ }
47
+ /** A project view whose dev properties are resolved for `name`. */
48
+ export function environmentProject(project, name) {
49
+ if (!project.devProperties)
50
+ return project;
51
+ return {
52
+ ...project,
53
+ devProperties: resolveEnvironment(project.devProperties, name),
54
+ };
55
+ }
56
+ /**
57
+ * Write one environment's override into the base: keep only the keys whose
58
+ * value differs from the base, so the file stays minimal.
59
+ */
60
+ export function withEnvironmentOverride(base, name, values) {
61
+ const current = { ...base.environments?.[name] };
62
+ const changed = Object.fromEntries(Object.entries(values).map(([key, value]) => {
63
+ const baseValue = base[key];
64
+ const same = value === undefined ||
65
+ value === '' ||
66
+ JSON.stringify(value) === JSON.stringify(baseValue);
67
+ return [key, same ? undefined : value];
68
+ }));
69
+ const override = Object.fromEntries(Object.entries({ ...current, ...changed }).filter(([, value]) => value !== undefined));
70
+ return {
71
+ ...base,
72
+ environments: { ...base.environments, [name]: override },
73
+ };
74
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * UI language. English strings are the keys; `t()` returns them untouched in
3
+ * English and looks them up in the Swedish table otherwise, falling back to
4
+ * English for anything untranslated. `{name}` placeholders are filled from
5
+ * the vars argument.
6
+ */
7
+ export type Language = 'en' | 'sv';
8
+ export declare const LANGUAGES: Language[];
9
+ export declare const LANGUAGE_NAMES: Record<Language, string>;
10
+ export declare function getLanguage(): Language;
11
+ export declare function setLanguage(language: Language): void;
12
+ export declare function t(text: string, vars?: Record<string, string | number>): string;
@@ -0,0 +1,263 @@
1
+ export const LANGUAGES = ['en', 'sv'];
2
+ export const LANGUAGE_NAMES = {
3
+ en: 'English',
4
+ sv: 'Svenska',
5
+ };
6
+ let current = 'en';
7
+ export function getLanguage() {
8
+ return current;
9
+ }
10
+ export function setLanguage(language) {
11
+ current = language;
12
+ }
13
+ export function t(text, vars) {
14
+ let out = current === 'sv' ? (sv[text] ?? text) : text;
15
+ if (vars) {
16
+ for (const [name, value] of Object.entries(vars)) {
17
+ out = out.split(`{${name}}`).join(String(value));
18
+ }
19
+ }
20
+ return out;
21
+ }
22
+ const sv = {
23
+ // Frame
24
+ APP: 'APP',
25
+ 'WORKSPACE {n} apps': 'ARBETSYTA {n} appar',
26
+ 'WORKSPACE 1 app': 'ARBETSYTA 1 app',
27
+ '{arrow} {n} more': '{arrow} {n} till',
28
+ 'deps·config·sync·signing': 'beroenden·konfig·synk·sign',
29
+ 'Workspace settings': 'Arbetsyteinställningar',
30
+ TASKS: 'UPPGIFTER',
31
+ // Tabs
32
+ Overview: 'Översikt',
33
+ Config: 'Konfig',
34
+ Versions: 'Versioner',
35
+ Log: 'Logg',
36
+ Ovw: 'Övs',
37
+ Cfg: 'Kfg',
38
+ Ver: 'Ver',
39
+ id: 'id',
40
+ version: 'version',
41
+ type: 'typ',
42
+ addon: 'tillägg',
43
+ site: 'webbplats',
44
+ domain: 'domän',
45
+ auth: 'auth',
46
+ 'signing user': 'signeringsanv.',
47
+ 'not set': 'ej satt',
48
+ '↑ root': '↑ rot',
49
+ ' · bundled': ' · paketerad',
50
+ deps: 'beroenden',
51
+ config: 'konfig',
52
+ sync: 'synk',
53
+ signing: 'signering',
54
+ scripts: 'scripts',
55
+ 'missing · i to install': 'saknas · i för att installera',
56
+ 'not installed · i to install': 'ej installerat · i för att installera',
57
+ 'dev properties': 'dev-egenskaper',
58
+ 'missing · e to edit': 'saknas · e för att redigera',
59
+ '{n} diffs · y to apply': '{n} skillnader · y för att tillämpa',
60
+ '1 diff · y to apply': '1 skillnad · y för att tillämpa',
61
+ 'missing · / set up signing': 'saknas · / konfigurera signering',
62
+ 'not installed': 'ej installerat',
63
+ '⚠ plaintext password in .dev_properties.json · / migrate': '⚠ lösenord i klartext i .dev_properties.json · / migrera',
64
+ RECENT: 'SENASTE',
65
+ '⚠ Configure dev properties first (e).': '⚠ Konfigurera dev-egenskaper först (e).',
66
+ 'Press R to fetch versions from {domain}.': 'Tryck R för att hämta versioner från {domain}.',
67
+ 'APP IDENTIFIER': 'APP-ID',
68
+ VERSION: 'VERSION',
69
+ ACTIVE: 'AKTIV',
70
+ 'No versions uploaded to {addon}.': 'Inga versioner uppladdade till {addon}.',
71
+ '{n} versions · a activate selected · R refresh': '{n} versioner · a aktivera vald · R uppdatera',
72
+ '1 version · a activate selected · R refresh': '1 version · a aktivera vald · R uppdatera',
73
+ ' · fetched {time}': ' · hämtad {time}',
74
+ 'No task yet. d dev · w watch · b build · s sign · p deploy': 'Ingen uppgift ännu. d dev · w watch · b build · s sign · p deploy',
75
+ ' · following': ' · följer',
76
+ // Palette
77
+ SETUP: 'INSTÄLLNINGAR',
78
+ AUTH: 'AUTH',
79
+ Commands: 'Kommandon',
80
+ '{n} actions': '{n} åtgärder',
81
+ 'type to filter · ↑↓ move · Enter run · Esc close': 'skriv för att filtrera · ↑↓ flytta · Enter kör · Esc stäng',
82
+ // Addon picker
83
+ 'Addon Repository': 'Tilläggsförråd',
84
+ '{n} of {m}': '{n} av {m}',
85
+ 'fetching addons': 'hämtar tillägg',
86
+ '↑↓ move · Enter select · Esc cancel': '↑↓ flytta · Enter välj · Esc avbryt',
87
+ // Actions
88
+ Dev: 'Dev',
89
+ Watch: 'Watch',
90
+ Build: 'Build',
91
+ Sign: 'Sign',
92
+ Deploy: 'Driftsätt',
93
+ 'Deploy (force)': 'Driftsätt (tvinga)',
94
+ 'to {env}': 'till {env}',
95
+ 'Switch environment': 'Byt miljö',
96
+ 'Add environment': 'Lägg till miljö',
97
+ 'e.g. test or prod, overriding domain and auth': 't.ex. test eller prod, med egen domän och auth',
98
+ 'Environment name (e.g. test, prod)': 'Miljönamn (t.ex. test, prod)',
99
+ 'Deploy the signed {id} to {env} and activate it?': 'Driftsätt signerade {id} till {env} och aktivera?',
100
+ 'Dev never deploys to a production environment ({env}). Switch with E.': 'Dev driftsätter aldrig till en produktionsmiljö ({env}). Byt med E.',
101
+ 'switched to {env}': 'bytte till {env}',
102
+ 'environment {env} added': 'miljön {env} tillagd',
103
+ environment: 'miljö',
104
+ production: 'produktion',
105
+ env: 'miljö',
106
+ '↑ dev': '↑ dev',
107
+ 'Activate a remote version': 'Aktivera en fjärrversion',
108
+ 'Stop running task': 'Stoppa pågående uppgift',
109
+ 'Edit config': 'Redigera konfig',
110
+ 'Sync package.json': 'Synka package.json',
111
+ 'Migrate password to OS keychain': 'Flytta lösenord till nyckelringen',
112
+ 'Install dependencies': 'Installera beroenden',
113
+ 'Log in': 'Logga in',
114
+ 'Log out': 'Logga ut',
115
+ Quit: 'Avsluta',
116
+ Settings: 'Inställningar',
117
+ 'build, sign and deploy on change': 'bygg, signera och driftsätt vid ändring',
118
+ 'build and deploy on change': 'bygg och driftsätt vid ändring',
119
+ 'rebuild and sign only': 'bara bygg om och signera',
120
+ 'rebuild only': 'bara bygg om',
121
+ 'credentials missing': 'uppgifter saknas',
122
+ 'signed zip · confirms first': 'signerad zip · bekräftar först',
123
+ 'shared auth and site config for every app': 'delad auth och webbplatskonfig för alla appar',
124
+ 'dev properties, auth method, signing': 'dev-egenskaper, auth-metod, signering',
125
+ '{n} diffs': '{n} skillnader',
126
+ '1 diff': '1 skillnad',
127
+ 'in sync': 'synkad',
128
+ 'forget stored credentials for this site': 'glöm sparade uppgifter för webbplatsen',
129
+ 'language, intro animation, workspace config': 'språk, introanimation, arbetsytans konfig',
130
+ 'Workspace config': 'Arbetsytans konfig',
131
+ 'not configured': 'ej konfigurerad',
132
+ 'basic · password needed': 'basic · lösenord krävs',
133
+ 'oauth2 · not logged in': 'oauth2 · ej inloggad',
134
+ 'sso · not logged in': 'sso · ej inloggad',
135
+ 'Dev properties not configured. Edit config first.': 'Dev-egenskaper ej konfigurerade. Redigera konfig först.',
136
+ 'Deploy password for {user}@{domain}': 'Driftsättningslösenord för {user}@{domain}',
137
+ 'Save to OS keychain': 'Spara i nyckelringen',
138
+ 'Signing credentials not configured.': 'Signeringsuppgifter ej konfigurerade.',
139
+ 'Signing password for {user} (developer.sitevision.se)': 'Signeringslösenord för {user} (developer.sitevision.se)',
140
+ 'A dev/watch task is already running for this app (K stops it).': 'En dev/watch-uppgift körs redan för appen (K stoppar den).',
141
+ 'stopped {n} task(s)': 'stoppade {n} uppgift(er)',
142
+ 'nothing running': 'inget körs',
143
+ 'Not in a workspace: run svc at the repo root.': 'Ingen arbetsyta: kör svc i repots rot.',
144
+ 'package.json updated': 'package.json uppdaterad',
145
+ 'password moved to keychain': 'lösenord flyttat till nyckelringen',
146
+ 'could not access keychain': 'kunde inte nå nyckelringen',
147
+ 'logged in': 'inloggad',
148
+ 'credentials removed': 'uppgifter borttagna',
149
+ // Config form
150
+ 'Development domain': 'Utvecklingsdomän',
151
+ 'Site name': 'Webbplatsnamn',
152
+ 'Addon name': 'Tilläggsnamn',
153
+ Username: 'Användarnamn',
154
+ 'Auth method': 'Auth-metod',
155
+ Password: 'Lösenord',
156
+ 'OAuth2 client id': 'OAuth2 klient-id',
157
+ 'Authorization endpoint': 'Auktoriseringsendpoint',
158
+ 'Token endpoint': 'Token-endpoint',
159
+ Scopes: 'Scopes',
160
+ 'Client secret': 'Klienthemlighet',
161
+ 'Login URL': 'Inloggnings-URL',
162
+ 'Use HTTP': 'Använd HTTP',
163
+ 'Signing user': 'Signeringsanvändare',
164
+ Certificate: 'Certifikat',
165
+ 'Signing password': 'Signeringslösenord',
166
+ '^O pick from repo': '^O välj från förråd',
167
+ 'Space-separated scopes to request. ALL grants the Sitevision API and offline_access adds a refresh token so later runs log in silently. Match the casing your client expects.': 'Scopes att begära, separerade med mellanslag. ALL ger Sitevision-API:et och offline_access ger en refresh-token så att senare körningar loggar in tyst. Använd det skiftläge klienten förväntar sig.',
168
+ 'blank = site root': 'tomt = webbplatsens rot',
169
+ 'required for signed deploys': 'krävs för signerad driftsättning',
170
+ 'blank = prompt each run': 'tomt = fråga varje gång',
171
+ SIGNING: 'SIGNERING',
172
+ ENVIRONMENT: 'MILJÖ',
173
+ 'Environment name': 'Miljönamn',
174
+ Production: 'Produktion',
175
+ 'What this base configuration is: dev, test, prod… Other environments are added on top of it with E or the palette and override only what differs.': 'Vad denna grundkonfiguration är: dev, test, prod… Andra miljöer läggs ovanpå med E eller paletten och skriver bara över det som skiljer.',
176
+ 'Treat deploys to this base environment as production: signed zip, confirmation, activation, and no dev loop. Off by default even when the name says prod, so a repo with only a production site still gets a dev loop.': 'Behandla driftsättningar till grundmiljön som produktion: signerad zip, bekräftelse, aktivering och ingen dev-loop. Av som standard även om namnet säger prod, så att ett repo med bara en produktionssajt ändå får en dev-loop.',
177
+ 'Saved {label}.': 'Sparade {label}.',
178
+ 'Looking up OAuth2 endpoints…': 'Slår upp OAuth2-endpoints…',
179
+ 'Endpoints filled from the site OpenID config.': 'Endpoints ifyllda från webbplatsens OpenID-konfig.',
180
+ 'Could not discover OAuth2 endpoints; enter them by hand.': 'Kunde inte hitta OAuth2-endpoints; ange dem manuellt.',
181
+ '✗ required': '✗ krävs',
182
+ keychain: 'nyckelring',
183
+ local: 'lokal',
184
+ '•••••••• keychain': '•••••••• nyckelring',
185
+ FIELD: 'FÄLT',
186
+ VALUE: 'VÄRDE',
187
+ SOURCE: 'KÄLLA',
188
+ "Shared by every app below {root}. An app's own value wins.": 'Delas av alla appar under {root}. Appens eget värde vinner.',
189
+ 'PACKAGE.JSON SYNC': 'PACKAGE.JSON-SYNK',
190
+ '←→ choose · Enter confirm · Esc cancel': '←→ välj · Enter bekräfta · Esc avbryt',
191
+ 'Enter save · Esc cancel': 'Enter spara · Esc avbryt',
192
+ '↑↓ field · Enter edit · ^O pick addon': '↑↓ fält · Enter redigera · ^O välj tillägg',
193
+ '↑↓ field · Enter edit': '↑↓ fält · Enter redigera',
194
+ 'Tab to edit': 'Tab för att redigera',
195
+ yes: 'ja',
196
+ no: 'nej',
197
+ // Shell
198
+ workspace: 'arbetsyta',
199
+ settings: 'inställningar',
200
+ save: 'spara',
201
+ cancel: 'avbryt',
202
+ field: 'fält',
203
+ edit: 'redigera',
204
+ back: 'tillbaka',
205
+ quit: 'avsluta',
206
+ 'edit settings': 'redigera inställningar',
207
+ apps: 'appar',
208
+ activate: 'aktivera',
209
+ refresh: 'uppdatera',
210
+ deploy: 'driftsätt',
211
+ commands: 'kommandon',
212
+ follow: 'följ',
213
+ wrap: 'radbryt',
214
+ stop: 'stoppa',
215
+ 'pick addon': 'välj tillägg',
216
+ login: 'logga in',
217
+ install: 'installera',
218
+ '{v} is already active': '{v} är redan aktiv',
219
+ '{v} activated': '{v} aktiverad',
220
+ 'No credentials.': 'Inga uppgifter.',
221
+ 'not available': 'ej tillgänglig',
222
+ '{n} tasks': '{n} uppgifter',
223
+ idle: 'inaktiv',
224
+ 'workspace config saved': 'arbetsytans konfig sparad',
225
+ 'config saved': 'konfig sparad',
226
+ ' · shared .dev_properties.json at the root': ' · delad .dev_properties.json i roten',
227
+ 'y confirm · n cancel': 'y bekräfta · n avbryt',
228
+ // Config help
229
+ 'Domain of the development environment (USE or TSE) without https://, e.g. myorg-use.sitevision-cloud.se. Deploys and version lookups go here.': 'Domän för utvecklingsmiljön (USE eller TSE) utan https://, t.ex. myorg-use.sitevision-cloud.se. Driftsättningar och versionslistor går hit.',
230
+ "Name of the site's root node in Sitevision, exactly as shown in the site tree. It becomes part of the REST API path.": 'Namnet på webbplatsens rotnod i Sitevision, exakt som i webbplatsträdet. Det blir en del av REST API-sökvägen.',
231
+ "Name of the addon (custom module) in the site's Addon Repository that this app is uploaded into. Ctrl+O lists the existing ones.": 'Namnet på tillägget (custom module) i webbplatsens tilläggsförråd som appen laddas upp till. Ctrl+O listar befintliga.',
232
+ 'Sitevision account used for deploys, usually your Sitevision Cloud e-mail. It needs DEVELOPER or MANAGE_ADDONS permission on the site.': 'Sitevision-konto som används för driftsättning, oftast din Sitevision Cloud-e-post. Behöver DEVELOPER eller MANAGE_ADDONS på webbplatsen.',
233
+ "How deploys authenticate: basic = username and password; oauth2 = bearer token from the site's OAuth2 provider (PKCE, opens a browser); cookie = reuse a browser SSO/SAML session.": 'Hur driftsättningar autentiseras: basic = användarnamn och lösenord; oauth2 = bearer-token från webbplatsens OAuth2-provider (PKCE, öppnar webbläsare); cookie = återanvänd en SSO/SAML-session från webbläsaren.',
234
+ 'Deploy password for the account above. Stored in the OS keychain, never in a file. Leave empty to be asked on each run.': 'Driftsättningslösenord för kontot ovan. Sparas i nyckelringen, aldrig i en fil. Lämna tomt för att bli tillfrågad varje gång.',
235
+ 'Client id of the OAuth2 client registered on the site. Its redirect URI must be http://127.0.0.1:8137/callback.': 'Klient-id för OAuth2-klienten som är registrerad på webbplatsen. Dess redirect-URI måste vara http://127.0.0.1:8137/callback.',
236
+ "The provider's authorization URL. Filled in from the site's OpenID configuration when it can be discovered.": 'Providerns auktoriserings-URL. Fylls i från webbplatsens OpenID-konfiguration när den kan hittas.',
237
+ "The provider's token URL. Filled in from the site's OpenID configuration when it can be discovered.": 'Providerns token-URL. Fylls i från webbplatsens OpenID-konfiguration när den kan hittas.',
238
+ 'Secret of a confidential OAuth2 client, stored in the OS keychain. Leave empty for a public client.': 'Hemlighet för en konfidentiell OAuth2-klient, sparas i nyckelringen. Lämna tomt för en publik klient.',
239
+ 'Page opened in the browser for the SSO login. Leave empty to use the site root.': 'Sida som öppnas i webbläsaren för SSO-inloggning. Tomt = webbplatsens rot.',
240
+ 'Use plain HTTP instead of HTTPS for deploys. Only for local or test servers without TLS.': 'Använd HTTP i stället för HTTPS vid driftsättning. Bara för lokala servrar eller testservrar utan TLS.',
241
+ 'Your developer.sitevision.se account. Production deploys need the app signed by it.': 'Ditt konto på developer.sitevision.se. Produktionsdriftsättningar kräver att appen är signerad av det.',
242
+ 'Which certificate to sign with when your developer account has several. Leave empty for the default.': 'Vilket certifikat som ska signera när utvecklarkontot har flera. Tomt = standard.',
243
+ 'Password for the signing account, stored in the OS keychain. Leave empty to be asked on each run.': 'Lösenord för signeringskontot, sparas i nyckelringen. Lämna tomt för att bli tillfrågad varje gång.',
244
+ // Settings screen
245
+ Language: 'Språk',
246
+ 'Intro animation': 'Introanimation',
247
+ on: 'på',
248
+ off: 'av',
249
+ 'Saved.': 'Sparat.',
250
+ 'stored in {file}': 'sparas i {file}',
251
+ '↑↓ setting · Enter edit · Esc close': '↑↓ inställning · Enter redigera · Esc stäng',
252
+ // Password prompt / login
253
+ 'Press Enter to submit, Esc to cancel': 'Tryck Enter för att skicka, Esc för att avbryta',
254
+ '(Tab to toggle)': '(Tab växlar)',
255
+ 'OAuth2 login': 'OAuth2-inloggning',
256
+ 'Session cookie login': 'Sessionskaka-inloggning',
257
+ 'Waiting for you to finish login in the browser…': 'Väntar på att du loggar in i webbläsaren…',
258
+ "If the browser didn't open, visit:": 'Om webbläsaren inte öppnades, gå till:',
259
+ 'Opening browser…': 'Öppnar webbläsare…',
260
+ 'Log in in the opened browser, then press Enter to capture the session.': 'Logga in i webbläsaren och tryck sedan Enter för att fånga sessionen.',
261
+ 'Capturing session…': 'Fångar session…',
262
+ 'Press Esc to cancel.': 'Tryck Esc för att avbryta.',
263
+ };
@@ -1,6 +1,7 @@
1
1
  import type { DevProperties } from '../types/index.js';
2
2
  /** Default loopback port. Fixed so a single redirect URI can be whitelisted. */
3
3
  export declare const DEFAULT_REDIRECT_PORT = 8137;
4
+ export declare const DEFAULT_SCOPES: string[];
4
5
  /** RFC 7636 S256 pair. Exported for testing the challenge derivation. */
5
6
  export declare function createPkcePair(): {
6
7
  verifier: string;
@@ -1,10 +1,13 @@
1
1
  import http from 'http';
2
2
  import crypto from 'crypto';
3
- import { spawn } from 'child_process';
3
+ import open from 'open';
4
4
  import { makeRequest, summarizeErrorBody } from './sitevision-api.js';
5
5
  import { getOAuth2RefreshToken, setOAuth2RefreshToken, deleteOAuth2RefreshToken, getOAuth2ClientSecret, } from './keychain.js';
6
6
  /** Default loopback port. Fixed so a single redirect URI can be whitelisted. */
7
7
  export const DEFAULT_REDIRECT_PORT = 8137;
8
+ // Sitevision's provider grants everything under ALL; offline_access adds the
9
+ // refresh token so later runs log in silently.
10
+ export const DEFAULT_SCOPES = ['ALL', 'offline_access'];
8
11
  /** Seconds to wait for the user to finish logging in before giving up. */
9
12
  const LOGIN_TIMEOUT_MS = 300_000;
10
13
  function base64url(buffer) {
@@ -81,15 +84,11 @@ export async function discoverOAuth2Config(domain, useHTTP = false) {
81
84
  }
82
85
  }
83
86
  export function openBrowser(url) {
84
- const isWin = process.platform === 'win32';
85
- const cmd = process.platform === 'darwin' ? 'open' : isWin ? 'cmd' : 'xdg-open';
86
- const args = isWin ? ['/c', 'start', '', url] : [url];
87
- try {
88
- spawn(cmd, args, { stdio: 'ignore', detached: true }).unref();
89
- }
90
- catch {
87
+ // Preserve the full OAuth URL, including & and percent-encoded parameters,
88
+ // through Windows shell parsing. `open` handles platform-specific escaping.
89
+ void open(url).catch(() => {
91
90
  // Fall back to the printed URL.
92
- }
91
+ });
93
92
  }
94
93
  /**
95
94
  * Turn a redirect's query params into a result, prioritizing the provider's own
@@ -185,9 +184,7 @@ export function beginOAuth2Login(dev) {
185
184
  url.searchParams.set('state', state);
186
185
  url.searchParams.set('code_challenge', challenge);
187
186
  url.searchParams.set('code_challenge_method', 'S256');
188
- if (config.scopes?.length) {
189
- url.searchParams.set('scope', config.scopes.join(' '));
190
- }
187
+ url.searchParams.set('scope', (config.scopes ?? DEFAULT_SCOPES).join(' '));
191
188
  const loopback = startLoopback(port, state);
192
189
  const complete = async () => {
193
190
  const redirect = await loopback.result;
@@ -22,6 +22,14 @@ export declare function findDevPropertiesPath(root: string): string | null;
22
22
  * Get the default dev properties path (for creating new files)
23
23
  */
24
24
  export declare function getDefaultDevPropertiesPath(root: string): string;
25
+ /**
26
+ * The dev properties a workspace root defines (its own file merged over any
27
+ * ancestors'), with the deploy password resolved from the keychain like an
28
+ * app's would be. Used to edit shared config from the shell.
29
+ */
30
+ export declare function readWorkspaceDevProperties(root: string): Partial<DevProperties>;
31
+ /** Dev properties inherited from ancestor directories only (no own file). */
32
+ export declare function readInheritedDevProperties(root: string): Partial<DevProperties>;
25
33
  /**
26
34
  * Get the full app ID including any prefix/suffix from environment
27
35
  *
@@ -81,12 +89,25 @@ export declare function readManifest(cwd: string): {
81
89
  * Detect if the current directory is a Sitevision project
82
90
  */
83
91
  export declare function detectProject(cwd?: string): ProjectInfo | null;
92
+ /**
93
+ * Fill the runtime-only credential fields for the given domain/username:
94
+ * deploy password (env var > keychain), OAuth2 access token (env var), and
95
+ * session cookie (env var > keychain). Mutates and returns `dev`.
96
+ */
97
+ export declare function resolveRuntimeSecrets(dev: DevProperties): DevProperties;
84
98
  /**
85
99
  * Validate that we're in a Sitevision project directory
86
100
  */
87
101
  export declare function requireProject(cwd?: string): ProjectInfo;
88
102
  /**
89
- * Get the app type (web, widget, rest)
103
+ * The app type (web, widget, rest, mcp), or undefined for a manifest type
104
+ * this CLI does not know. Display code uses this so one odd app never takes
105
+ * the whole shell down.
106
+ */
107
+ export declare function appTypeOf(manifest: SitevisionManifest): SimpleAppType | undefined;
108
+ /**
109
+ * Get the app type (web, widget, rest, mcp). Throws for unknown types, since
110
+ * build and deploy cannot proceed without knowing the endpoints.
90
111
  */
91
112
  export declare function getAppType(manifest: SitevisionManifest): SimpleAppType;
92
113
  /**
@@ -109,6 +130,7 @@ export declare function writeDevProperties(projectRoot: string, properties: DevP
109
130
  */
110
131
  export interface SvcConfig {
111
132
  syncPackageJson?: boolean;
133
+ environment?: string;
112
134
  [key: string]: unknown;
113
135
  }
114
136
  export declare function readSvcConfig(projectRoot: string): SvcConfig;