@signageos/cli 3.0.0 → 4.0.0

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.
@@ -200,7 +200,7 @@ exports.appletUpload = (0, commandDefinition_1.createCommandDefinition)({
200
200
  if (!(error instanceof appletErrors_1.AppletDoesNotExistError)) {
201
201
  throw error;
202
202
  }
203
- const createdApplet = yield restApi.applet.create({ name: appletName });
203
+ const createdApplet = yield restApi.applet.create({ name: appletName, organizationUid });
204
204
  appletUid = createdApplet.uid;
205
205
  (0, log_1.log)('info', chalk_1.default.yellow(`Applet uid is not present in package file.`, chalk_1.default.blue(`\nCreated new uid:`), chalk_1.default.green(appletUid)), `\n`);
206
206
  }
@@ -0,0 +1,8 @@
1
+ import { Auth0Settings } from '@signageos/cli-common';
2
+ /**
3
+ * Auth0 settings for the signageOS CLI tool (regular API).
4
+ *
5
+ * These connect to the regular signageOS API (not admin API).
6
+ * Override with environment variables for testing or custom Auth0 tenants.
7
+ */
8
+ export declare function getAuth0Settings(): Auth0Settings;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getAuth0Settings = getAuth0Settings;
4
+ /**
5
+ * Auth0 settings for the signageOS CLI tool (regular API).
6
+ *
7
+ * These connect to the regular signageOS API (not admin API).
8
+ * Override with environment variables for testing or custom Auth0 tenants.
9
+ */
10
+ function getAuth0Settings() {
11
+ var _a, _b, _c, _d;
12
+ return {
13
+ domain: (_a = process.env.SOS_AUTH0_DOMAIN) !== null && _a !== void 0 ? _a : 'sos-production.us.auth0.com',
14
+ clientId: (_b = process.env.SOS_AUTH0_CLIENT_ID) !== null && _b !== void 0 ? _b : '8AU8D3zJ4mK8gszZP3gZO0nv9DusSNjV',
15
+ audience: (_c = process.env.SOS_AUTH0_AUDIENCE) !== null && _c !== void 0 ? _c : 'https://sos-production.us.auth0.com/api/v2/',
16
+ scope: (_d = process.env.SOS_AUTH0_SCOPE) !== null && _d !== void 0 ? _d : 'openid profile email offline_access',
17
+ };
18
+ }
@@ -1,45 +1,25 @@
1
- import { CommandLineOptions } from '../Command/commandDefinition';
2
- declare const OPTION_LIST: readonly [{
3
- readonly name: "username";
4
- readonly type: StringConstructor;
5
- readonly description: "Username or e-mail used for authentication";
6
- }];
7
1
  /**
8
- * To explicitly enable auth0 authentication add flag --auth0-enabled to command line options
9
- * { _unknown: [ '--auth0-enabled' ], command: [ 'login' ] }
10
- */
11
- export declare const getIsAuth0Enabled: (options: any) => {
12
- isAuth0Enabled?: boolean;
13
- };
14
- /**
15
- * Handles user authentication using username/email and password credentials.
16
- * Supports Auth0 authentication method. Stores credentials securely in the
17
- * ~/.sosrc configuration file for subsequent CLI operations.
2
+ * Authenticates the user via the Auth0 Device Authorization Flow.
3
+ * Opens a browser-based verification page where the user logs in,
4
+ * then stores the resulting JWT tokens in `~/.sosrc`.
18
5
  *
19
6
  * @group Authentication:1
20
7
  *
21
8
  * @example
22
9
  * ```bash
23
- * # Interactive login (prompts for username and password)
10
+ * # Interactive login (opens browser for Auth0 authentication)
24
11
  * sos login
25
12
  *
26
- * # Login with username specified
27
- * sos login --username user@example.com
13
+ * # Login with a specific profile
14
+ * sos --profile staging login
28
15
  * ```
29
16
  *
30
- * @throws {Error} When username is missing and not provided interactively
31
- *
32
- * @since 0.3.0
17
+ * @since 4.0.0
33
18
  */
34
19
  export declare const login: {
35
20
  name: "login";
36
21
  description: string;
37
- optionList: readonly [{
38
- readonly name: "username";
39
- readonly type: StringConstructor;
40
- readonly description: "Username or e-mail used for authentication";
41
- }];
22
+ optionList: never[];
42
23
  commands: never[];
43
- run(options: CommandLineOptions<typeof OPTION_LIST>): Promise<void>;
24
+ run(): Promise<void>;
44
25
  };
45
- export {};
@@ -1,37 +1,4 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
3
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
4
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -45,74 +12,63 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
45
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
46
13
  };
47
14
  Object.defineProperty(exports, "__esModule", { value: true });
48
- exports.login = exports.getIsAuth0Enabled = void 0;
15
+ exports.login = void 0;
16
+ const node_child_process_1 = require("node:child_process");
17
+ const node_os_1 = require("node:os");
49
18
  const chalk_1 = __importDefault(require("chalk"));
50
19
  const prompts_1 = __importDefault(require("prompts"));
51
- const debug_1 = __importDefault(require("debug"));
52
- const os = __importStar(require("os"));
53
20
  const fs_extra_1 = __importDefault(require("fs-extra"));
54
- const apiVersions_1 = require("@signageos/sdk/dist/RestApi/apiVersions");
55
21
  const log_1 = require("@signageos/sdk/dist/Console/log");
56
22
  const sosControlHelper_1 = require("@signageos/sdk/dist/SosHelper/sosControlHelper");
57
- const helper_1 = require("../helper");
58
- const runControlHelper_1 = require("../RunControl/runControlHelper");
59
- const parameters_1 = require("../parameters");
23
+ const cli_common_1 = require("@signageos/cli-common");
24
+ const auth0Settings_1 = require("./auth0Settings");
60
25
  const globalArgs_1 = require("../Command/globalArgs");
61
26
  const commandDefinition_1 = require("../Command/commandDefinition");
62
- const Debug = (0, debug_1.default)('@signageos/cli:Auth:login');
63
- const OPTION_LIST = [{ name: 'username', type: String, description: `Username or e-mail used for authentication` }];
64
- /**
65
- * To explicitly enable auth0 authentication add flag --auth0-enabled to command line options
66
- * { _unknown: [ '--auth0-enabled' ], command: [ 'login' ] }
67
- */
68
- const getIsAuth0Enabled = (options) => {
69
- var _a;
70
- const queryParams = {};
71
- if ((_a = options._unknown) === null || _a === void 0 ? void 0 : _a.includes('--auth0-enabled')) {
72
- queryParams.isAuth0Enabled = true;
73
- }
74
- return queryParams;
75
- };
76
- exports.getIsAuth0Enabled = getIsAuth0Enabled;
27
+ /** Best-effort attempt to open a URL in the user's default browser. */
28
+ function openInBrowser(url) {
29
+ const cmd = (0, node_os_1.platform)() === 'darwin'
30
+ ? `open ${JSON.stringify(url)}`
31
+ : (0, node_os_1.platform)() === 'win32'
32
+ ? `start "" ${JSON.stringify(url)}`
33
+ : `xdg-open ${JSON.stringify(url)}`;
34
+ (0, node_child_process_1.exec)(cmd, (err) => {
35
+ if (err) {
36
+ // Silently ignore the URL is already printed to the console as a fallback.
37
+ }
38
+ });
39
+ }
77
40
  /**
78
- * Handles user authentication using username/email and password credentials.
79
- * Supports Auth0 authentication method. Stores credentials securely in the
80
- * ~/.sosrc configuration file for subsequent CLI operations.
41
+ * Authenticates the user via the Auth0 Device Authorization Flow.
42
+ * Opens a browser-based verification page where the user logs in,
43
+ * then stores the resulting JWT tokens in `~/.sosrc`.
81
44
  *
82
45
  * @group Authentication:1
83
46
  *
84
47
  * @example
85
48
  * ```bash
86
- * # Interactive login (prompts for username and password)
49
+ * # Interactive login (opens browser for Auth0 authentication)
87
50
  * sos login
88
51
  *
89
- * # Login with username specified
90
- * sos login --username user@example.com
52
+ * # Login with a specific profile
53
+ * sos --profile staging login
91
54
  * ```
92
55
  *
93
- * @throws {Error} When username is missing and not provided interactively
94
- *
95
- * @since 0.3.0
56
+ * @since 4.0.0
96
57
  */
97
58
  exports.login = (0, commandDefinition_1.createCommandDefinition)({
98
59
  name: 'login',
99
- description: 'Authenticate user with signageOS',
100
- optionList: OPTION_LIST,
60
+ description: 'Authenticate user with signageOS via Auth0',
61
+ optionList: [],
101
62
  commands: [],
102
- run(options) {
63
+ run() {
103
64
  return __awaiter(this, void 0, void 0, function* () {
104
- let identification = options.username;
65
+ var _a;
105
66
  const profile = (0, globalArgs_1.getGlobalProfile)();
106
67
  const configFilePath = (0, sosControlHelper_1.getConfigFilePath)();
107
68
  // Detect a new (non-existent) named profile and prompt for the API URL
108
- let promptedApiUrl;
109
69
  if (profile) {
110
- let profileExists = false;
111
- if (yield fs_extra_1.default.pathExists(configFilePath)) {
112
- const content = (yield fs_extra_1.default.readFile(configFilePath)).toString();
113
- profileExists = content.includes(`[profile ${profile}]`);
114
- }
115
- if (!profileExists) {
70
+ const exists = (yield fs_extra_1.default.pathExists(configFilePath)) && (0, cli_common_1.profileExists)(profile);
71
+ if (!exists) {
116
72
  (0, log_1.log)('info', `Profile "${profile}" does not exist in ${configFilePath}. Please enter the server API URL to create it.`);
117
73
  const { inputApiUrl } = yield (0, prompts_1.default)({
118
74
  type: 'text',
@@ -124,74 +80,27 @@ exports.login = (0, commandDefinition_1.createCommandDefinition)({
124
80
  if (!inputApiUrl) {
125
81
  throw new Error('API URL is required to log in.');
126
82
  }
127
- promptedApiUrl = inputApiUrl.replace(/\/+$/, '');
83
+ (0, cli_common_1.writeProfileField)('apiUrl', inputApiUrl.replace(/\/+$/, ''), profile);
128
84
  }
129
85
  }
130
- const config = yield (0, runControlHelper_1.loadConfig)();
131
- const apiUrl = promptedApiUrl !== null && promptedApiUrl !== void 0 ? promptedApiUrl : (0, helper_1.getApiUrl)(config);
132
- // Extract domain from API URL to show in prompts
133
- const hostToDisplay = new URL(apiUrl).hostname;
134
- if (!identification) {
135
- const response = yield (0, prompts_1.default)({
136
- type: 'text',
137
- name: 'username',
138
- message: `Type your username or e-mail used for ${hostToDisplay}`,
139
- });
140
- identification = response.username;
141
- }
142
- if (!identification) {
143
- throw new Error('Missing argument --username <string>');
86
+ const auth0 = (0, auth0Settings_1.getAuth0Settings)();
87
+ (0, log_1.log)('info', 'Starting Auth0 Device Authorization Flow...');
88
+ const deviceCode = yield (0, cli_common_1.requestDeviceCode)(auth0);
89
+ const verificationUrl = (_a = deviceCode.verification_uri_complete) !== null && _a !== void 0 ? _a : deviceCode.verification_uri;
90
+ // Try to open the verification URL in the default browser
91
+ openInBrowser(verificationUrl);
92
+ console.info('');
93
+ console.info(chalk_1.default.bold('To authenticate, open this URL in your browser:'));
94
+ console.info(chalk_1.default.cyan.underline(verificationUrl));
95
+ console.info('');
96
+ if (!deviceCode.verification_uri_complete) {
97
+ console.info(`Then enter this code: ${chalk_1.default.bold.yellow(deviceCode.user_code)}`);
98
+ console.info('');
144
99
  }
145
- const { password } = yield (0, prompts_1.default)({
146
- type: 'password',
147
- name: 'password',
148
- message: `Type your password used for ${hostToDisplay}`,
149
- });
150
- const authQueryParams = (0, exports.getIsAuth0Enabled)(options);
151
- const { id: tokenId, securityToken: apiSecurityToken, name, } = yield getOrCreateApiSecurityToken(Object.assign({ identification,
152
- password,
153
- apiUrl }, authQueryParams));
154
- yield (0, runControlHelper_1.saveConfig)({
155
- apiUrl: profile || apiUrl !== parameters_1.parameters.apiUrl ? apiUrl : undefined,
156
- identification: tokenId,
157
- apiSecurityToken,
158
- });
159
- (0, log_1.log)('info', `User ${chalk_1.default.green(identification)} has been logged in with token "${name}". Credentials are stored in ${chalk_1.default.blue((0, sosControlHelper_1.getConfigFilePath)())}`);
100
+ console.info(chalk_1.default.dim(`Waiting for authorization (expires in ${Math.round(deviceCode.expires_in / 60)} minutes)...`));
101
+ const tokens = yield (0, cli_common_1.pollForToken)(auth0, deviceCode.device_code, deviceCode.interval, deviceCode.expires_in);
102
+ (0, cli_common_1.saveStoredTokens)(tokens, profile);
103
+ (0, log_1.log)('info', `Successfully authenticated. Credentials are stored in ${chalk_1.default.blue(configFilePath)}`);
160
104
  });
161
105
  },
162
106
  });
163
- function getOrCreateApiSecurityToken(_a) {
164
- return __awaiter(this, arguments, void 0, function* ({ identification, password, apiUrl, isAuth0Enabled, }) {
165
- var _b;
166
- const ACCOUNT_SECURITY_TOKEN_RESOURCE = 'account/security-token';
167
- const options = {
168
- url: apiUrl,
169
- auth: { clientId: undefined, secret: undefined },
170
- version: apiVersions_1.ApiVersions.V1,
171
- };
172
- const tokenName = generateTokenName();
173
- const requestBody = Object.assign({ identification,
174
- password, name: tokenName }, (isAuth0Enabled !== undefined ? { isAuth0AuthenticationEnabled: isAuth0Enabled } : {}));
175
- const response = yield (0, helper_1.postResource)(options, ACCOUNT_SECURITY_TOKEN_RESOURCE, null, requestBody);
176
- const responseBody = JSON.parse(yield response.text(), helper_1.deserializeJSON);
177
- // Don't log sensitive response data
178
- Debug('POST security-token response status', response.status);
179
- if (response.status === 201) {
180
- return responseBody;
181
- }
182
- else if (response.status === 403) {
183
- throw new Error(`Incorrect username or password`);
184
- }
185
- else {
186
- // Ensure password is not logged in error messages
187
- const errorMessage = (_b = responseBody === null || responseBody === void 0 ? void 0 : responseBody.message) !== null && _b !== void 0 ? _b : `HTTP status ${response.status}`;
188
- throw new Error('Unknown error: ' + errorMessage);
189
- }
190
- });
191
- }
192
- function generateTokenName() {
193
- const hostname = os.hostname();
194
- const shortHostname = hostname.split('.')[0];
195
- const userInfo = os.userInfo();
196
- return `${userInfo.username}@${shortHostname}`;
197
- }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Logs the user out by clearing stored Auth0 tokens from `~/.sosrc`.
3
+ * Non-auth fields (apiUrl, defaultOrganizationUid, emulatorUid) are preserved.
4
+ *
5
+ * @group Authentication:2
6
+ *
7
+ * @example
8
+ * ```bash
9
+ * # Logout from default profile
10
+ * sos logout
11
+ *
12
+ * # Logout from a specific profile
13
+ * sos --profile staging logout
14
+ * ```
15
+ *
16
+ * @since 4.0.0
17
+ */
18
+ export declare const logout: {
19
+ name: "logout";
20
+ description: string;
21
+ optionList: never[];
22
+ commands: never[];
23
+ run(): Promise<void>;
24
+ };
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.logout = void 0;
16
+ const chalk_1 = __importDefault(require("chalk"));
17
+ const log_1 = require("@signageos/sdk/dist/Console/log");
18
+ const sosControlHelper_1 = require("@signageos/sdk/dist/SosHelper/sosControlHelper");
19
+ const cli_common_1 = require("@signageos/cli-common");
20
+ const globalArgs_1 = require("../Command/globalArgs");
21
+ const commandDefinition_1 = require("../Command/commandDefinition");
22
+ /**
23
+ * Logs the user out by clearing stored Auth0 tokens from `~/.sosrc`.
24
+ * Non-auth fields (apiUrl, defaultOrganizationUid, emulatorUid) are preserved.
25
+ *
26
+ * @group Authentication:2
27
+ *
28
+ * @example
29
+ * ```bash
30
+ * # Logout from default profile
31
+ * sos logout
32
+ *
33
+ * # Logout from a specific profile
34
+ * sos --profile staging logout
35
+ * ```
36
+ *
37
+ * @since 4.0.0
38
+ */
39
+ exports.logout = (0, commandDefinition_1.createCommandDefinition)({
40
+ name: 'logout',
41
+ description: 'Log out from signageOS (clear stored tokens)',
42
+ optionList: [],
43
+ commands: [],
44
+ run() {
45
+ return __awaiter(this, void 0, void 0, function* () {
46
+ const profile = (0, globalArgs_1.getGlobalProfile)();
47
+ (0, cli_common_1.clearStoredTokens)(profile);
48
+ const configFilePath = (0, sosControlHelper_1.getConfigFilePath)();
49
+ (0, log_1.log)('info', `Logged out. Auth tokens removed from ${chalk_1.default.blue(configFilePath)}.`);
50
+ });
51
+ },
52
+ });
@@ -2,62 +2,62 @@
2
2
 
3
3
  # Extract command path from completion words array
4
4
  _sos_extract_cmd_path() {
5
- local result=""
6
- for ((i=1; i<$1; i++)); do
7
- if [[ ${COMP_WORDS[i]} != -* ]]; then
8
- if [[ -n "$result" ]]; then
9
- result="${result} ${COMP_WORDS[i]}"
10
- else
11
- result="${COMP_WORDS[i]}"
12
- fi
13
- fi
14
- done
15
- echo "$result"
5
+ local result=""
6
+ for ((i = 1; i < $1; i++)); do
7
+ if [[ ${COMP_WORDS[i]} != -* ]]; then
8
+ if [[ -n "$result" ]]; then
9
+ result="${result} ${COMP_WORDS[i]}"
10
+ else
11
+ result="${COMP_WORDS[i]}"
12
+ fi
13
+ fi
14
+ done
15
+ echo "$result"
16
16
  }
17
17
 
18
18
  # Common completion logic
19
19
  _sos_completion_impl() {
20
- local cur="$1"
21
- local prev="$2"
22
- local cmd_path="$3"
23
-
24
- # Handle direct command completion or empty command path
25
- if [[ "${cur}" == *sos* || -z "$cmd_path" ]]; then
26
- COMPREPLY=( $(compgen -W "${TOPLEVEL_COMMANDS}" -- "$cur") )
27
- return 0
28
- fi
29
-
30
- # We need to find the subcommands for the current command path
31
- # This will be added by the generateCompletionScript function
32
- # with specific command paths and their subcommands
33
- case "$cmd_path" in
34
- # COMMAND_SCHEMA_CASES will be replaced with actual cases during generation
35
- *)
36
- # Default to top-level commands
37
- COMPREPLY=( $(compgen -W "${TOPLEVEL_COMMANDS}" -- "$cur") )
38
- ;;
39
- esac
20
+ local cur="$1"
21
+ local prev="$2"
22
+ local cmd_path="$3"
23
+
24
+ # Handle direct command completion or empty command path
25
+ if [[ "${cur}" == *sos* || -z "$cmd_path" ]]; then
26
+ COMPREPLY=($(compgen -W "${TOPLEVEL_COMMANDS}" -- "$cur"))
27
+ return 0
28
+ fi
29
+
30
+ # We need to find the subcommands for the current command path
31
+ # This will be added by the generateCompletionScript function
32
+ # with specific command paths and their subcommands
33
+ case "$cmd_path" in
34
+ # COMMAND_SCHEMA_CASES will be replaced with actual cases during generation
35
+ *)
36
+ # Default to top-level commands
37
+ COMPREPLY=($(compgen -W "${TOPLEVEL_COMMANDS}" -- "$cur"))
38
+ ;;
39
+ esac
40
40
  }
41
41
 
42
42
  # Completion function with bash-completion
43
43
  _sos_completion() {
44
- local cur prev words cword split
45
- _init_completion -s || return
44
+ local cur prev words cword split
45
+ _init_completion -s || return
46
46
 
47
- cmd_path=$(_sos_extract_cmd_path $COMP_CWORD)
48
- _sos_completion_impl "${COMP_WORDS[COMP_CWORD]}" "${COMP_WORDS[COMP_CWORD-1]}" "$cmd_path"
47
+ cmd_path=$(_sos_extract_cmd_path $COMP_CWORD)
48
+ _sos_completion_impl "${COMP_WORDS[COMP_CWORD]}" "${COMP_WORDS[COMP_CWORD - 1]}" "$cmd_path"
49
49
  }
50
50
 
51
51
  # Fallback completion for systems without bash-completion
52
52
  _sos_completion_fallback() {
53
- COMPREPLY=()
54
- cmd_path=$(_sos_extract_cmd_path $COMP_CWORD)
55
- _sos_completion_impl "${COMP_WORDS[COMP_CWORD]}" "${COMP_WORDS[COMP_CWORD-1]}" "$cmd_path"
53
+ COMPREPLY=()
54
+ cmd_path=$(_sos_extract_cmd_path $COMP_CWORD)
55
+ _sos_completion_impl "${COMP_WORDS[COMP_CWORD]}" "${COMP_WORDS[COMP_CWORD - 1]}" "$cmd_path"
56
56
  }
57
57
 
58
58
  # Register the appropriate completion function
59
- if command -v _init_completion >/dev/null 2>&1; then
60
- complete -F _sos_completion sos
59
+ if command -v _init_completion > /dev/null 2>&1; then
60
+ complete -F _sos_completion sos
61
61
  else
62
- complete -F _sos_completion_fallback sos
63
- fi
62
+ complete -F _sos_completion_fallback sos
63
+ fi
@@ -73,7 +73,7 @@ exports.customScriptUpload = (0, commandDefinition_1.createCommandDefinition)({
73
73
  const organization = yield (0, organizationFacade_1.getOrganization)(organizationUid);
74
74
  const restApi = yield (0, helper_1.createOrganizationRestApi)(organization);
75
75
  const config = yield (0, customScriptFacade_1.getConfig)(currentDirectory);
76
- const customScriptVersion = yield (0, customScriptFacade_1.ensureCustomScriptVersion)(restApi, config, skipConfirmation);
76
+ const customScriptVersion = yield (0, customScriptFacade_1.ensureCustomScriptVersion)(restApi, config, skipConfirmation, organizationUid);
77
77
  for (const platform of Object.keys(config.platforms)) {
78
78
  const platformConfig = config.platforms[platform];
79
79
  if (!platformConfig) {
@@ -117,7 +117,7 @@ export declare function getConfig(workDir: string): Promise<{
117
117
  * Add data to the config file .sosconfig.json
118
118
  */
119
119
  export declare function addToConfigFile(workDir: string, data: Partial<CustomScriptConfig>): Promise<void>;
120
- export declare function ensureCustomScriptVersion(restApi: RestApi, config: CustomScriptConfig, skipConfirmation?: boolean): Promise<ICustomScriptVersion | import("@signageos/sdk/dist/RestApi/CustomScript/Version/CustomScriptVersion").CustomScriptVersion>;
120
+ export declare function ensureCustomScriptVersion(restApi: RestApi, config: CustomScriptConfig, skipConfirmation?: boolean, organizationUid?: string): Promise<ICustomScriptVersion | import("@signageos/sdk/dist/RestApi/CustomScript/Version/CustomScriptVersion").CustomScriptVersion>;
121
121
  export declare function uploadCode({ restApi, workDir, platform, config, customScriptVersion, }: {
122
122
  restApi: RestApi;
123
123
  workDir: string;
@@ -127,10 +127,10 @@ function loadConfigFromFile(workDir) {
127
127
  function getConfigFilePath(workDir) {
128
128
  return path.join(workDir, fileSystem_1.SOS_CONFIG_FILE_NAME);
129
129
  }
130
- function ensureCustomScriptVersion(restApi, config, skipConfirmation) {
130
+ function ensureCustomScriptVersion(restApi, config, skipConfirmation, organizationUid) {
131
131
  return __awaiter(this, void 0, void 0, function* () {
132
132
  var _a;
133
- const customScript = yield ensureCustomScript(restApi, config, skipConfirmation);
133
+ const customScript = yield ensureCustomScript(restApi, config, skipConfirmation, organizationUid);
134
134
  const customScriptVersion = yield restApi.customScript.version.get({
135
135
  customScriptUid: customScript.uid,
136
136
  version: config.version,
@@ -160,7 +160,7 @@ function ensureCustomScriptVersion(restApi, config, skipConfirmation) {
160
160
  });
161
161
  });
162
162
  }
163
- function ensureCustomScript(restApi, config, skipConfirmation) {
163
+ function ensureCustomScript(restApi, config, skipConfirmation, organizationUid) {
164
164
  return __awaiter(this, void 0, void 0, function* () {
165
165
  if (config.uid) {
166
166
  const customScript = yield restApi.customScript.get(config.uid);
@@ -194,6 +194,7 @@ function ensureCustomScript(restApi, config, skipConfirmation) {
194
194
  title: config.name, // TODO change
195
195
  description: config.description,
196
196
  dangerLevel: config.dangerLevel ? config.dangerLevel : 'normal', // default to 'normal' if not provided
197
+ organizationUid,
197
198
  });
198
199
  // TODO ask for permission or read from CLI arg
199
200
  (0, log_1.log)('info', chalk_1.default.yellow('Adding Custom Script uid to the config file'));
@@ -87,6 +87,7 @@ exports.setContent = (0, commandDefinition_1.createCommandDefinition)({
87
87
  type: 'DURATION',
88
88
  data: '1000',
89
89
  },
90
+ organizationUid,
90
91
  });
91
92
  (0, log_1.log)('info', chalk_1.default.green(`Applet ${appletUid} was set on device ${deviceUid}`));
92
93
  });
@@ -109,6 +109,7 @@ function disconnectDevice(organization, deviceUid) {
109
109
  clientId: organization.oauthClientId,
110
110
  secret: organization.oauthClientSecret,
111
111
  },
112
+ accessToken: config.accessToken,
112
113
  version: apiVersions_1.ApiVersions.V1,
113
114
  };
114
115
  const responseOfPost = yield (0, helper_1.postResource)(options, DEVICE_RESOURCE, null, { deviceUid: `${deviceUid}` });
@@ -25,12 +25,12 @@ const log_1 = require("@signageos/sdk/dist/Console/log");
25
25
  const paginationHelper_1 = require("../helper/paginationHelper");
26
26
  const createRestApi = (config) => {
27
27
  var _a, _b;
28
+ const auth = config.accessToken
29
+ ? { accessToken: config.accessToken }
30
+ : { clientId: (_a = config.identification) !== null && _a !== void 0 ? _a : '', secret: (_b = config.apiSecurityToken) !== null && _b !== void 0 ? _b : '' };
28
31
  const options = {
29
32
  url: (0, helper_1.getApiUrl)(config),
30
- auth: {
31
- clientId: (_a = config.identification) !== null && _a !== void 0 ? _a : '',
32
- secret: (_b = config.apiSecurityToken) !== null && _b !== void 0 ? _b : '',
33
- },
33
+ auth,
34
34
  version: apiVersions_1.ApiVersions.V1,
35
35
  clientVersions: (0, helper_1.createClientVersions)(),
36
36
  };
@@ -70,7 +70,7 @@ function loadEmulatorOrCreateNewAndReturnUid(organizationUid) {
70
70
  return __awaiter(this, void 0, void 0, function* () {
71
71
  var _a, _b, _c, _d, _e, _f, _g, _h;
72
72
  const config = yield (0, runControlHelper_1.loadConfig)();
73
- if (!config.identification || !config.apiSecurityToken) {
73
+ if (!config.accessToken && (!config.identification || !config.apiSecurityToken)) {
74
74
  throw new Error(`No authenticized account found. Try to login using ${chalk_1.default.green('sos login')}`);
75
75
  }
76
76
  const restApi = createRestApi(config);
@@ -127,6 +127,7 @@ function getOrganizations() {
127
127
  clientId: config.identification,
128
128
  secret: config.apiSecurityToken,
129
129
  },
130
+ accessToken: config.accessToken,
130
131
  version: apiVersions_1.ApiVersions.V1,
131
132
  };
132
133
  const responseOfGet = yield (0, helper_1.getResource)(options, ORGANIZATION_RESOURCE);
@@ -153,6 +154,7 @@ function getOrganization(organizationUid) {
153
154
  clientId: config.identification,
154
155
  secret: config.apiSecurityToken,
155
156
  },
157
+ accessToken: config.accessToken,
156
158
  version: apiVersions_1.ApiVersions.V1,
157
159
  };
158
160
  const responseOfGet = yield (0, helper_1.getResource)(options, ORGANIZATION_RESOURCE + '/' + organizationUid);
@@ -66,7 +66,7 @@ exports.pluginUpload = (0, commandDefinition_1.createCommandDefinition)({
66
66
  const config = yield (0, pluginFacade_1.getSosConfig)(currentDirectory);
67
67
  const schema = yield (0, pluginFacade_1.loadSchemas)(currentDirectory);
68
68
  const skipConfirmation = !!options.yes;
69
- const pluginVersion = yield (0, pluginFacade_1.ensurePluginVersion)(restApi, config, schema, skipConfirmation);
69
+ const pluginVersion = yield (0, pluginFacade_1.ensurePluginVersion)(restApi, config, schema, skipConfirmation, organizationUid);
70
70
  for (const platform of Object.keys(config.platforms)) {
71
71
  const platformConfig = config.platforms[platform];
72
72
  if (!platformConfig) {
@@ -2,7 +2,7 @@ import z from 'zod';
2
2
  import RestApi from '@signageos/sdk/dist/RestApi/RestApi';
3
3
  import { IPluginVersion } from '@signageos/sdk/dist/RestApi/Plugin/Version/IPluginVersion';
4
4
  import { PlatformConfig } from '../CustomScript/customScriptFacade';
5
- export declare function ensurePluginVersion(restApi: RestApi, config: PluginConfig, schema: any, skipConfirmation?: boolean): Promise<import("@signageos/sdk/dist/RestApi/Plugin/Version/PluginVersion").PluginVersion>;
5
+ export declare function ensurePluginVersion(restApi: RestApi, config: PluginConfig, schema: any, skipConfirmation?: boolean, organizationUid?: string): Promise<import("@signageos/sdk/dist/RestApi/Plugin/Version/PluginVersion").PluginVersion>;
6
6
  export declare function uploadCode({ restApi, workDir, platform, config, pluginVersion, }: {
7
7
  restApi: RestApi;
8
8
  workDir: string;
@@ -62,10 +62,10 @@ const archive_1 = require("../Lib/archive");
62
62
  const runtimeFileSystem_1 = require("@signageos/sdk/dist/Development/runtimeFileSystem");
63
63
  const customScriptFacade_1 = require("../CustomScript/customScriptFacade");
64
64
  const PLUGIN_BUILDS_DIRNAME = 'plugin_builds';
65
- function ensurePluginVersion(restApi, config, schema, skipConfirmation) {
65
+ function ensurePluginVersion(restApi, config, schema, skipConfirmation, organizationUid) {
66
66
  return __awaiter(this, void 0, void 0, function* () {
67
67
  var _a;
68
- const plugin = yield ensurePlugin(restApi, config, skipConfirmation);
68
+ const plugin = yield ensurePlugin(restApi, config, skipConfirmation, organizationUid);
69
69
  const pluginVersion = yield restApi.plugin.version.get({
70
70
  pluginUid: plugin.uid,
71
71
  version: config.version,
@@ -97,7 +97,7 @@ function ensurePluginVersion(restApi, config, schema, skipConfirmation) {
97
97
  });
98
98
  });
99
99
  }
100
- function ensurePlugin(restApi, config, skipConfirmation) {
100
+ function ensurePlugin(restApi, config, skipConfirmation, organizationUid) {
101
101
  return __awaiter(this, void 0, void 0, function* () {
102
102
  if (config.uid) {
103
103
  const plugin = yield restApi.plugin.get(config.uid);
@@ -129,6 +129,7 @@ function ensurePlugin(restApi, config, skipConfirmation) {
129
129
  name: config.name,
130
130
  title: config.name,
131
131
  description: config.description,
132
+ organizationUid,
132
133
  });
133
134
  // TODO ask for permission or read from CLI arg
134
135
  (0, log_1.log)('info', chalk_1.default.yellow('Adding Plugin uid to the config file'));
@@ -1,12 +1,13 @@
1
1
  import { IConfig } from '@signageos/sdk/dist/SosHelper/sosControlHelper';
2
- /** The same as loadConfig in SDK, but respect CLI --profile argument */
3
- export declare function loadConfig(): Promise<{
4
- apiUrl: string;
5
- identification?: string;
6
- apiSecurityToken?: string;
7
- defaultOrganizationUid?: string;
8
- emulatorUid?: string;
9
- }>;
2
+ /**
3
+ * Extended config interface that includes Auth0 JWT token alongside legacy fields.
4
+ * When `accessToken` is present, it takes precedence over `identification`/`apiSecurityToken`.
5
+ */
6
+ export interface IExtendedConfig extends IConfig {
7
+ accessToken?: string;
8
+ }
9
+ /** The same as loadConfig in SDK, but respect CLI --profile argument and Auth0 tokens */
10
+ export declare function loadConfig(): Promise<IExtendedConfig>;
10
11
  /** The same as saveConfig in SDK, but respect CLI --profile argument */
11
12
  export declare function saveConfig(newConfig: IConfig): Promise<void>;
12
13
  /** The same as updateConfig in SDK, but respect CLI --profile argument */
@@ -8,13 +8,20 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
11
14
  Object.defineProperty(exports, "__esModule", { value: true });
12
15
  exports.loadConfig = loadConfig;
13
16
  exports.saveConfig = saveConfig;
14
17
  exports.updateConfig = updateConfig;
18
+ const debug_1 = __importDefault(require("debug"));
15
19
  const sosControlHelper_1 = require("@signageos/sdk/dist/SosHelper/sosControlHelper");
20
+ const cli_common_1 = require("@signageos/cli-common");
16
21
  const globalArgs_1 = require("../Command/globalArgs");
17
- /** The same as loadConfig in SDK, but respect CLI --profile argument */
22
+ const auth0Settings_1 = require("../Auth/auth0Settings");
23
+ const Debug = (0, debug_1.default)('@signageos/cli:RunControl');
24
+ /** The same as loadConfig in SDK, but respect CLI --profile argument and Auth0 tokens */
18
25
  function loadConfig() {
19
26
  return __awaiter(this, void 0, void 0, function* () {
20
27
  const profile = (0, globalArgs_1.getGlobalProfile)();
@@ -36,6 +43,44 @@ function loadConfig() {
36
43
  envOverride.apiUrl = process.env.SOS_API_URL;
37
44
  }
38
45
  const finalConfig = Object.assign(Object.assign({}, config), envOverride);
46
+ // Check for SOS_ACCESS_TOKEN env var (CI-friendly JWT override)
47
+ const envToken = process.env.SOS_ACCESS_TOKEN;
48
+ if (envToken) {
49
+ Debug('Using access token from SOS_ACCESS_TOKEN env var');
50
+ finalConfig.accessToken = envToken;
51
+ return finalConfig;
52
+ }
53
+ // Try to load Auth0 tokens from ~/.sosrc
54
+ const tokens = (0, cli_common_1.loadStoredTokens)(profile);
55
+ if (tokens) {
56
+ if ((0, cli_common_1.isTokenExpired)(tokens)) {
57
+ Debug('Stored access token is expired (expiresAt: %s)', tokens.expiresAt);
58
+ if (tokens.refreshToken) {
59
+ try {
60
+ Debug('Attempting token refresh via Auth0');
61
+ const auth0 = (0, auth0Settings_1.getAuth0Settings)();
62
+ const refreshed = yield (0, cli_common_1.refreshAccessToken)(auth0, tokens.refreshToken);
63
+ (0, cli_common_1.saveStoredTokens)(refreshed, profile);
64
+ finalConfig.accessToken = refreshed.accessToken;
65
+ Debug('Token refreshed successfully (new expiresAt: %s)', refreshed.expiresAt);
66
+ }
67
+ catch (error) {
68
+ Debug('Token refresh failed: %s', error instanceof Error ? error.message : String(error));
69
+ // Token refresh failed — fall through to legacy auth or require re-login
70
+ }
71
+ }
72
+ else {
73
+ Debug('No refresh token available, cannot refresh');
74
+ }
75
+ }
76
+ else {
77
+ Debug('Using stored access token (expiresAt: %s)', tokens.expiresAt);
78
+ finalConfig.accessToken = tokens.accessToken;
79
+ }
80
+ }
81
+ else {
82
+ Debug('No stored Auth0 tokens found, using legacy auth');
83
+ }
39
84
  return finalConfig;
40
85
  });
41
86
  }
@@ -67,7 +67,7 @@ exports.runnerUpload = (0, commandDefinition_1.createCommandDefinition)({
67
67
  const config = yield (0, pluginFacade_1.getSosConfig)(currentDirectory);
68
68
  const schema = yield (0, runnerFacade_1.loadSchemas)(currentDirectory);
69
69
  const skipConfirmation = !!options.yes;
70
- const runnerVersion = yield (0, runnerFacade_1.ensureRunnerVersion)(restApi, config, schema, skipConfirmation);
70
+ const runnerVersion = yield (0, runnerFacade_1.ensureRunnerVersion)(restApi, config, schema, skipConfirmation, organizationUid);
71
71
  for (const platform of Object.keys(config.platforms)) {
72
72
  const platformConfig = config.platforms[platform];
73
73
  if (!platformConfig) {
@@ -3,7 +3,7 @@ import { PlatformConfig } from '../CustomScript/customScriptFacade';
3
3
  import { IRunnerVersion } from '@signageos/sdk/dist/RestApi/Runner/Version/IRunnerVersion';
4
4
  import z from 'zod';
5
5
  import { ConfigSchema } from '../Plugin/pluginFacade';
6
- export declare function ensureRunnerVersion(restApi: RestApi, config: RunnerConfig, schema: any, skipConfirmation?: boolean): Promise<IRunnerVersion>;
6
+ export declare function ensureRunnerVersion(restApi: RestApi, config: RunnerConfig, schema: any, skipConfirmation?: boolean, organizationUid?: string): Promise<IRunnerVersion>;
7
7
  export declare function uploadCode({ restApi, workDir, platform, config, runnerVersion, }: {
8
8
  restApi: RestApi;
9
9
  workDir: string;
@@ -59,10 +59,10 @@ const fileSystem_1 = require("../Lib/fileSystem");
59
59
  const runtimeFileSystem_1 = require("@signageos/sdk/dist/Development/runtimeFileSystem");
60
60
  const customScriptFacade_1 = require("../CustomScript/customScriptFacade");
61
61
  const PLUGIN_BUILDS_DIRNAME = 'plugin_builds';
62
- function ensureRunnerVersion(restApi, config, schema, skipConfirmation) {
62
+ function ensureRunnerVersion(restApi, config, schema, skipConfirmation, organizationUid) {
63
63
  return __awaiter(this, void 0, void 0, function* () {
64
64
  var _a;
65
- const runner = yield ensureRunner(restApi, config, skipConfirmation);
65
+ const runner = yield ensureRunner(restApi, config, skipConfirmation, organizationUid);
66
66
  const runnerVersion = yield restApi.runner.version.get({ runnerUid: runner.uid, version: config.version });
67
67
  if (runnerVersion) {
68
68
  return runnerVersion;
@@ -93,7 +93,7 @@ function ensureRunnerVersion(restApi, config, schema, skipConfirmation) {
93
93
  });
94
94
  });
95
95
  }
96
- function ensureRunner(restApi, config, skipConfirmation) {
96
+ function ensureRunner(restApi, config, skipConfirmation, organizationUid) {
97
97
  return __awaiter(this, void 0, void 0, function* () {
98
98
  if (config.uid) {
99
99
  const runner = yield restApi.runner.get(config.uid);
@@ -125,6 +125,7 @@ function ensureRunner(restApi, config, skipConfirmation) {
125
125
  name: config.name,
126
126
  title: config.name,
127
127
  description: config.description,
128
+ organizationUid,
128
129
  });
129
130
  // TODO ask for permission or read from CLI arg
130
131
  (0, log_1.log)('info', chalk_1.default.yellow('Adding Runner uid to the config file'));
package/dist/helper.d.ts CHANGED
@@ -1,14 +1,14 @@
1
1
  import prompts from 'prompts';
2
2
  import RestApi from '@signageos/sdk/dist/RestApi/RestApi';
3
+ import { IExtendedConfig } from './RunControl/runControlHelper';
3
4
  import { ApiVersions } from '@signageos/sdk/dist/RestApi/apiVersions';
4
- import { IConfig } from '@signageos/sdk/dist/SosHelper/sosControlHelper';
5
5
  type RequestInit = globalThis.RequestInit;
6
6
  interface ICredentials {
7
7
  oauthClientId: string;
8
8
  oauthClientSecret: string;
9
9
  }
10
10
  export declare function loadApiUrl(): Promise<string>;
11
- export declare function getApiUrl(config: IConfig): string;
11
+ export declare function getApiUrl(config: IExtendedConfig): string;
12
12
  export declare function createClientVersions(): {
13
13
  signageOS_CLI: any;
14
14
  };
@@ -20,6 +20,8 @@ export interface IOptions {
20
20
  clientId: string | undefined;
21
21
  secret: string | undefined;
22
22
  };
23
+ /** JWT access token — when set, used as `X-Auth: <token>` (takes precedence over clientId:secret) */
24
+ accessToken?: string;
23
25
  version: ApiVersions;
24
26
  headers?: {
25
27
  [name: string]: string;
package/dist/helper.js CHANGED
@@ -59,12 +59,13 @@ function createClientVersions() {
59
59
  }
60
60
  function createOrganizationRestApi(credentials) {
61
61
  return __awaiter(this, void 0, void 0, function* () {
62
+ const config = yield (0, runControlHelper_1.loadConfig)();
63
+ const auth = config.accessToken
64
+ ? { accessToken: config.accessToken }
65
+ : { clientId: credentials.oauthClientId, secret: credentials.oauthClientSecret };
62
66
  const options = {
63
67
  url: yield loadApiUrl(),
64
- auth: {
65
- clientId: credentials.oauthClientId,
66
- secret: credentials.oauthClientSecret,
67
- },
68
+ auth,
68
69
  version: apiVersions_1.ApiVersions.V1,
69
70
  clientVersions: createClientVersions(),
70
71
  };
@@ -74,8 +75,11 @@ function createOrganizationRestApi(credentials) {
74
75
  }
75
76
  exports.AUTH_HEADER = 'X-Auth';
76
77
  function createOptions(method, options, data) {
78
+ var _a, _b;
79
+ // Prefer JWT accessToken when available; fall back to legacy clientId:secret
80
+ const authValue = options.accessToken ? options.accessToken : ((_a = options.auth.clientId) !== null && _a !== void 0 ? _a : '') + ':' + ((_b = options.auth.secret) !== null && _b !== void 0 ? _b : '');
77
81
  return {
78
- headers: Object.assign({ 'Content-Type': 'application/json', [exports.AUTH_HEADER]: options.auth.clientId + ':' + options.auth.secret }, (options.headers || {})),
82
+ headers: Object.assign({ 'Content-Type': 'application/json', [exports.AUTH_HEADER]: authValue }, (options.headers || {})),
79
83
  method,
80
84
  body: typeof data !== 'undefined' ? JSON.stringify(data) : undefined,
81
85
  };
package/dist/index.js CHANGED
@@ -12,6 +12,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
13
  const appletCommand_1 = require("./Applet/appletCommand");
14
14
  const loginCommand_1 = require("./Auth/loginCommand");
15
+ const logoutCommand_1 = require("./Auth/logoutCommand");
15
16
  const organizationCommand_1 = require("./Organization/organizationCommand");
16
17
  const timingCommand_1 = require("./Timing/timingCommand");
17
18
  const commandProcessor_1 = require("./Command/commandProcessor");
@@ -48,7 +49,7 @@ const index = (0, commandDefinition_1.createCommandDefinition)({
48
49
  name: 'sos',
49
50
  description: 'SignageOS CLI - The central command-line tool for deploying, managing, and debugging signageOS projects and devices',
50
51
  optionList: generalCommand_1.GENERAL_OPTION_LIST,
51
- commands: [appletCommand_1.applet, loginCommand_1.login, organizationCommand_1.organization, timingCommand_1.timing, deviceCommand_1.device, customScriptCommand_1.customScript, pluginCommand_1.plugin, runnerCommand_1.runner, autocompleteCommand_1.autocomplete],
52
+ commands: [appletCommand_1.applet, loginCommand_1.login, logoutCommand_1.logout, organizationCommand_1.organization, timingCommand_1.timing, deviceCommand_1.device, customScriptCommand_1.customScript, pluginCommand_1.plugin, runnerCommand_1.runner, autocompleteCommand_1.autocomplete],
52
53
  run() {
53
54
  return __awaiter(this, void 0, void 0, function* () {
54
55
  throw new Error('Unknown command');
package/docs/index.md CHANGED
@@ -90,10 +90,16 @@ access to all available command groups.
90
90
 
91
91
  #### `sos login`
92
92
 
93
- Authenticate user with signageOS
93
+ Authenticate user with signageOS via Auth0
94
94
 
95
95
  [→ See detailed documentation](/cli/login/)
96
96
 
97
+ #### `sos logout`
98
+
99
+ Log out from signageOS (clear stored tokens)
100
+
101
+ [→ See detailed documentation](/cli/logout/)
102
+
97
103
  ### Development
98
104
 
99
105
  #### `sos applet`
@@ -185,7 +191,8 @@ sos --api-url https://api.custom.signageos.io applet list
185
191
  ## Related Commands
186
192
 
187
193
  - [`sos applet`](/cli/applet/) - Applet development and management operations
188
- - [`sos login`](/cli/login/) - Authenticate user with signageOS
194
+ - [`sos login`](/cli/login/) - Authenticate user with signageOS via Auth0
195
+ - [`sos logout`](/cli/logout/) - Log out from signageOS (clear stored tokens)
189
196
  - [`sos organization`](/cli/organization/) - Organization management operations
190
197
  - [`sos timing`](/cli/timing/) - Timing management
191
198
  - [`sos device`](/cli/device/) - Device management
@@ -5,14 +5,14 @@ sidebar_position: 10
5
5
  ---
6
6
  # login
7
7
 
8
- Authenticate user with signageOS
8
+ Authenticate user with signageOS via Auth0
9
9
 
10
10
 
11
11
  ## Description
12
12
 
13
- Handles user authentication using username/email and password credentials.
14
- Supports Auth0 authentication method. Stores credentials securely in the
15
- ~/.sosrc configuration file for subsequent CLI operations.
13
+ Authenticates the user via the Auth0 Device Authorization Flow.
14
+ Opens a browser-based verification page where the user logs in,
15
+ then stores the resulting JWT tokens in `~/.sosrc`.
16
16
 
17
17
  ## Usage
18
18
 
@@ -20,20 +20,14 @@ Supports Auth0 authentication method. Stores credentials securely in the
20
20
  sos login [options]
21
21
  ```
22
22
 
23
- ## Options
24
-
25
- | Option | Description |
26
- | ------------ | --------------------------------------------------- |
27
- | `--username` | Username or e-mail used for authentication (string) |
28
-
29
23
  ## Examples
30
24
 
31
25
  ```bash
32
- # Interactive login (prompts for username and password)
26
+ # Interactive login (opens browser for Auth0 authentication)
33
27
  sos login
34
28
 
35
- # Login with username specified
36
- sos login --username user@example.com
29
+ # Login with a specific profile
30
+ sos --profile staging login
37
31
  ```
38
32
 
39
33
  ## Advanced Usage
@@ -91,7 +85,7 @@ Generate tokens at: https://box.signageos.io/settings
91
85
 
92
86
  ## Since
93
87
 
94
- 0.3.0
88
+ 4.0.0
95
89
 
96
90
  ## Global Options
97
91
 
@@ -0,0 +1,62 @@
1
+ ---
2
+ id: logout-index
3
+ title: logout
4
+ sidebar_position: 20
5
+ ---
6
+ # logout
7
+
8
+ Log out from signageOS (clear stored tokens)
9
+
10
+
11
+ ## Description
12
+
13
+ Logs the user out by clearing stored Auth0 tokens from `~/.sosrc`.
14
+ Non-auth fields (apiUrl, defaultOrganizationUid, emulatorUid) are preserved.
15
+
16
+ ## Usage
17
+
18
+ ```bash
19
+ sos logout [options]
20
+ ```
21
+
22
+ ## Examples
23
+
24
+ ```bash
25
+ # Logout from default profile
26
+ sos logout
27
+
28
+ # Logout from a specific profile
29
+ sos --profile staging logout
30
+ ```
31
+
32
+ ## Since
33
+
34
+ 4.0.0
35
+
36
+ ## Global Options
37
+
38
+ All commands support the following global options:
39
+
40
+ | Option | Alias | Description |
41
+ |--------|-------|-------------|
42
+ | `--help` | `-h` | Display help information for any command |
43
+ | `--version` | `-v` | Display the installed version of the CLI |
44
+ | `--api-url` | `-u` | Override the API URL for REST requests |
45
+ | `--profile` | | Use a specific profile from ~/.sosrc config |
46
+
47
+ ### Examples
48
+
49
+ ```bash
50
+ # Show version
51
+ sos --version
52
+
53
+ # Get help for any command
54
+ sos applet --help
55
+ sos applet upload --help
56
+
57
+ # Use custom API endpoint
58
+ sos --api-url https://api.example.com applet upload
59
+
60
+ # Use specific profile
61
+ sos --profile production organization list
62
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signageos/cli",
3
- "version": "3.0.0",
3
+ "version": "4.0.0",
4
4
  "main": "./dist/index.js",
5
5
  "author": "signageOS.io <dev@signageos.io>",
6
6
  "files": [
@@ -54,7 +54,7 @@
54
54
  },
55
55
  "devDependencies": {
56
56
  "@istanbuljs/nyc-config-typescript": "1.0.2",
57
- "@signageos/codestyle": "2.1.0",
57
+ "@signageos/codestyle": "2.3.0",
58
58
  "@types/archiver": "6.0.3",
59
59
  "@types/child-process-promise": "2.2.6",
60
60
  "@types/cli-progress": "3.11.6",
@@ -85,8 +85,9 @@
85
85
  "unzipper": "0.12.3"
86
86
  },
87
87
  "dependencies": {
88
+ "@signageos/cli-common": "0.1.0",
88
89
  "@signageos/file": "2.0.1",
89
- "@signageos/sdk": "2.5.0",
90
+ "@signageos/sdk": "2.6.0",
90
91
  "archiver": "7.0.1",
91
92
  "chalk": "2.4.2",
92
93
  "child-process-promise": "2.1.3",