@oussema_mili/test-pkg-123 1.1.22

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.

Potentially problematic release.


This version of @oussema_mili/test-pkg-123 might be problematic. Click here for more details.

Files changed (49) hide show
  1. package/LICENSE +29 -0
  2. package/README.md +220 -0
  3. package/auth-callback.html +97 -0
  4. package/auth.js +276 -0
  5. package/cli-commands.js +1923 -0
  6. package/containerManager.js +304 -0
  7. package/daemon/agentRunner.js +429 -0
  8. package/daemon/daemonEntry.js +64 -0
  9. package/daemon/daemonManager.js +271 -0
  10. package/daemon/logManager.js +227 -0
  11. package/dist/styles.css +504 -0
  12. package/docker-actions/apps.js +3938 -0
  13. package/docker-actions/config-transformer.js +380 -0
  14. package/docker-actions/containers.js +355 -0
  15. package/docker-actions/general.js +171 -0
  16. package/docker-actions/images.js +1128 -0
  17. package/docker-actions/logs.js +224 -0
  18. package/docker-actions/metrics.js +270 -0
  19. package/docker-actions/registry.js +1100 -0
  20. package/docker-actions/setup-tasks.js +859 -0
  21. package/docker-actions/terminal.js +247 -0
  22. package/docker-actions/volumes.js +696 -0
  23. package/helper-functions.js +193 -0
  24. package/index.html +83 -0
  25. package/index.js +341 -0
  26. package/package.json +82 -0
  27. package/postcss.config.mjs +5 -0
  28. package/scripts/release.sh +212 -0
  29. package/setup/setupWizard.js +403 -0
  30. package/store/agentSessionStore.js +51 -0
  31. package/store/agentStore.js +113 -0
  32. package/store/configStore.js +171 -0
  33. package/store/daemonStore.js +217 -0
  34. package/store/deviceCredentialStore.js +107 -0
  35. package/store/npmTokenStore.js +65 -0
  36. package/store/registryStore.js +329 -0
  37. package/store/setupState.js +147 -0
  38. package/styles.css +1 -0
  39. package/utils/appLogger.js +223 -0
  40. package/utils/deviceInfo.js +98 -0
  41. package/utils/ecrAuth.js +225 -0
  42. package/utils/encryption.js +112 -0
  43. package/utils/envSetup.js +44 -0
  44. package/utils/errorHandler.js +327 -0
  45. package/utils/portUtils.js +59 -0
  46. package/utils/prerequisites.js +323 -0
  47. package/utils/prompts.js +318 -0
  48. package/utils/ssl-certificates.js +256 -0
  49. package/websocket-server.js +415 -0
@@ -0,0 +1,113 @@
1
+ import os from 'os';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { loadConfig } from './configStore.js';
5
+
6
+ const fsPromises = fs.promises;
7
+
8
+ // Load configuration
9
+ const config = loadConfig();
10
+ const AGENT_ROOT_DIR = config.agentRootDir;
11
+ const AGENT_INFO_DIR = 'agent';
12
+ const AGENT_INFO_FILE = 'info.json';
13
+ const AGENT_INFO_PATH = path.join(
14
+ os.homedir(),
15
+ AGENT_ROOT_DIR,
16
+ AGENT_INFO_DIR,
17
+ AGENT_INFO_FILE
18
+ );
19
+
20
+ /**
21
+ * Agent Store for managing persistent agent information
22
+ */
23
+ class AgentStore {
24
+ constructor() {
25
+ this.agentInfo = null;
26
+ this.initialized = false;
27
+ }
28
+
29
+ /**
30
+ * Initialize the store
31
+ */
32
+ async initialize() {
33
+ if (this.initialized) return;
34
+
35
+ try {
36
+ // Ensure directory exists
37
+ await fsPromises.mkdir(path.dirname(AGENT_INFO_PATH), { recursive: true });
38
+ this.initialized = true;
39
+ } catch (error) {
40
+ console.error('Failed to initialize agent store:', error);
41
+ throw error;
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Save agent start time
47
+ */
48
+ async saveAgentStartTime(startTime) {
49
+ await this.initialize();
50
+
51
+ const agentInfo = {
52
+ startTime: startTime.toISOString(),
53
+ lastUpdated: new Date().toISOString(),
54
+ };
55
+
56
+ try {
57
+ await fsPromises.writeFile(AGENT_INFO_PATH, JSON.stringify(agentInfo, null, 2));
58
+ this.agentInfo = agentInfo;
59
+ } catch (error) {
60
+ console.error('Failed to save agent start time:', error);
61
+ throw error;
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Load agent start time
67
+ */
68
+ async loadAgentStartTime() {
69
+ await this.initialize();
70
+
71
+ try {
72
+ const data = await fsPromises.readFile(AGENT_INFO_PATH, 'utf8');
73
+ const agentInfo = JSON.parse(data);
74
+ this.agentInfo = agentInfo;
75
+ return new Date(agentInfo.startTime);
76
+ } catch (error) {
77
+ if (error.code === 'ENOENT') {
78
+ // File doesn't exist - agent not running or first time
79
+ return null;
80
+ }
81
+ console.error('Failed to load agent start time:', error);
82
+ throw error;
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Clear agent info (when agent stops)
88
+ */
89
+ async clearAgentInfo() {
90
+ await this.initialize();
91
+
92
+ try {
93
+ await fsPromises.unlink(AGENT_INFO_PATH);
94
+ this.agentInfo = null;
95
+ } catch (error) {
96
+ if (error.code !== 'ENOENT') {
97
+ console.error('Failed to clear agent info:', error);
98
+ }
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Check if agent is currently running based on stored info
104
+ */
105
+ async isAgentRunning() {
106
+ const startTime = await this.loadAgentStartTime();
107
+ return startTime !== null;
108
+ }
109
+ }
110
+
111
+ // Export singleton instance
112
+ const agentStore = new AgentStore();
113
+ export default agentStore;
@@ -0,0 +1,171 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import os from "os";
4
+
5
+ const FENWAVE_DIR = path.join(os.homedir(), ".fenwave");
6
+ const CONFIG_DIR = path.join(FENWAVE_DIR, "config");
7
+ const CONFIG_FILE = path.join(CONFIG_DIR, "agent.json");
8
+
9
+ /**
10
+ * Default configuration values
11
+ */
12
+ const DEFAULT_CONFIG = {
13
+ // Fenwave URLs
14
+ backendUrl: "http://localhost:7007",
15
+ frontendUrl: "http://localhost:3000",
16
+
17
+ // Container Configuration
18
+ containerName: "fenwave-devapp",
19
+ containerPort: 3003,
20
+ wsPort: 3001,
21
+
22
+ // Directory Configuration
23
+ agentRootDir: ".fenwave",
24
+ registriesDir: "registries",
25
+ containerDataDir: "/data",
26
+
27
+ // Docker Image (GHCR - public registry, no auth required)
28
+ dockerImage: "ghcr.io/fenleap/fenwave/dev-app:latest",
29
+
30
+ // Other Settings
31
+ authTimeoutMs: 60000,
32
+ };
33
+
34
+ /**
35
+ * Ensure config directory exists
36
+ */
37
+ function ensureConfigDir() {
38
+ if (!fs.existsSync(FENWAVE_DIR)) {
39
+ fs.mkdirSync(FENWAVE_DIR, { recursive: true, mode: 0o700 });
40
+ }
41
+ if (!fs.existsSync(CONFIG_DIR)) {
42
+ fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Load configuration from disk
48
+ * @returns {Object} Configuration object with defaults applied
49
+ */
50
+ export function loadConfig() {
51
+ let config = { ...DEFAULT_CONFIG };
52
+
53
+ // Load from file if exists
54
+ if (fs.existsSync(CONFIG_FILE)) {
55
+ try {
56
+ const fileConfig = JSON.parse(fs.readFileSync(CONFIG_FILE, "utf8"));
57
+ config = { ...config, ...fileConfig };
58
+ } catch (error) {
59
+ console.error("Failed to load agent config:", error.message);
60
+ }
61
+ }
62
+
63
+ // Fall back to environment variables if config values are not set
64
+ config.backendUrl =
65
+ config.backendUrl || process.env.BACKEND_URL || DEFAULT_CONFIG.backendUrl;
66
+ config.frontendUrl =
67
+ config.frontendUrl ||
68
+ process.env.FRONTEND_URL ||
69
+ DEFAULT_CONFIG.frontendUrl;
70
+ config.wsPort =
71
+ config.wsPort || Number(process.env.WS_PORT) || DEFAULT_CONFIG.wsPort;
72
+ config.containerPort =
73
+ config.containerPort ||
74
+ Number(process.env.CONTAINER_PORT) ||
75
+ DEFAULT_CONFIG.containerPort;
76
+
77
+ return config;
78
+ }
79
+
80
+ /**
81
+ * Save configuration to disk
82
+ * @param {Object} config - Configuration object to save
83
+ */
84
+ export function saveConfig(config) {
85
+ ensureConfigDir();
86
+
87
+ const configToSave = {
88
+ ...config,
89
+ updatedAt: new Date().toISOString(),
90
+ };
91
+
92
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(configToSave, null, 2), {
93
+ mode: 0o600,
94
+ });
95
+ }
96
+
97
+ /**
98
+ * Update specific configuration values
99
+ * @param {Object} updates - Configuration updates
100
+ */
101
+ export function updateConfig(updates) {
102
+ const currentConfig = loadConfig();
103
+ const newConfig = { ...currentConfig, ...updates };
104
+ saveConfig(newConfig);
105
+ return newConfig;
106
+ }
107
+
108
+ /**
109
+ * Get a specific configuration value
110
+ * @param {string} key - Configuration key
111
+ * @param {any} defaultValue - Default value if key not found
112
+ * @returns {any} Configuration value
113
+ */
114
+ export function getConfig(key, defaultValue = null) {
115
+ const config = loadConfig();
116
+ return config[key] !== undefined ? config[key] : defaultValue;
117
+ }
118
+
119
+ /**
120
+ * Check if configuration file exists
121
+ * @returns {boolean} True if config file exists
122
+ */
123
+ export function configExists() {
124
+ return fs.existsSync(CONFIG_FILE);
125
+ }
126
+
127
+ /**
128
+ * Clear configuration file
129
+ */
130
+ export function clearConfig() {
131
+ if (fs.existsSync(CONFIG_FILE)) {
132
+ fs.unlinkSync(CONFIG_FILE);
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Get configuration file path
138
+ * @returns {string} Path to config file
139
+ */
140
+ export function getConfigPath() {
141
+ return CONFIG_FILE;
142
+ }
143
+
144
+ /**
145
+ * Initialize configuration with values from init wizard
146
+ * @param {Object} initConfig - Configuration from init wizard
147
+ */
148
+ export function initializeConfig(initConfig) {
149
+ const config = {
150
+ ...DEFAULT_CONFIG,
151
+ ...initConfig,
152
+ createdAt: new Date().toISOString(),
153
+ };
154
+
155
+ saveConfig(config);
156
+ return config;
157
+ }
158
+
159
+ export { DEFAULT_CONFIG };
160
+
161
+ export default {
162
+ loadConfig,
163
+ saveConfig,
164
+ updateConfig,
165
+ getConfig,
166
+ configExists,
167
+ clearConfig,
168
+ getConfigPath,
169
+ initializeConfig,
170
+ DEFAULT_CONFIG,
171
+ };
@@ -0,0 +1,217 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+
5
+ const FENWAVE_DIR = path.join(os.homedir(), '.fenwave');
6
+ const DAEMON_DIR = path.join(FENWAVE_DIR, 'daemon');
7
+ const STATE_FILE = path.join(DAEMON_DIR, 'state.json');
8
+ const PID_FILE = path.join(DAEMON_DIR, 'agent.pid');
9
+ const LOCK_FILE = path.join(DAEMON_DIR, 'agent.lock');
10
+
11
+ /**
12
+ * Ensure daemon directory exists
13
+ */
14
+ function ensureDaemonDir() {
15
+ if (!fs.existsSync(DAEMON_DIR)) {
16
+ fs.mkdirSync(DAEMON_DIR, { recursive: true, mode: 0o700 });
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Get daemon state
22
+ * @returns {Object|null} Daemon state or null if not found
23
+ */
24
+ export function getDaemonState() {
25
+ try {
26
+ if (fs.existsSync(STATE_FILE)) {
27
+ const data = fs.readFileSync(STATE_FILE, 'utf8');
28
+ return JSON.parse(data);
29
+ }
30
+ } catch (error) {
31
+ // Ignore errors reading state
32
+ }
33
+ return null;
34
+ }
35
+
36
+ /**
37
+ * Save daemon state
38
+ * @param {Object} state - State object to save
39
+ */
40
+ export function saveDaemonState(state) {
41
+ ensureDaemonDir();
42
+ const stateToSave = {
43
+ ...state,
44
+ updatedAt: new Date().toISOString(),
45
+ };
46
+ fs.writeFileSync(STATE_FILE, JSON.stringify(stateToSave, null, 2), {
47
+ mode: 0o600,
48
+ });
49
+ }
50
+
51
+ /**
52
+ * Update daemon state with partial updates
53
+ * @param {Object} updates - Partial state updates
54
+ */
55
+ export function updateDaemonState(updates) {
56
+ const currentState = getDaemonState() || {};
57
+ const newState = { ...currentState, ...updates };
58
+ saveDaemonState(newState);
59
+ return newState;
60
+ }
61
+
62
+ /**
63
+ * Clear daemon state
64
+ */
65
+ export function clearDaemonState() {
66
+ try {
67
+ if (fs.existsSync(STATE_FILE)) {
68
+ fs.unlinkSync(STATE_FILE);
69
+ }
70
+ } catch (error) {
71
+ // Ignore errors
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Get daemon PID
77
+ * @returns {number|null} PID or null if not found
78
+ */
79
+ export function getDaemonPid() {
80
+ try {
81
+ if (fs.existsSync(PID_FILE)) {
82
+ const pid = parseInt(fs.readFileSync(PID_FILE, 'utf8').trim(), 10);
83
+ return isNaN(pid) ? null : pid;
84
+ }
85
+ } catch (error) {
86
+ // Ignore errors
87
+ }
88
+ return null;
89
+ }
90
+
91
+ /**
92
+ * Save daemon PID
93
+ * @param {number} pid - Process ID
94
+ */
95
+ export function saveDaemonPid(pid) {
96
+ ensureDaemonDir();
97
+ fs.writeFileSync(PID_FILE, String(pid), { mode: 0o600 });
98
+ }
99
+
100
+ /**
101
+ * Clear daemon PID file
102
+ */
103
+ export function clearDaemonPid() {
104
+ try {
105
+ if (fs.existsSync(PID_FILE)) {
106
+ fs.unlinkSync(PID_FILE);
107
+ }
108
+ } catch (error) {
109
+ // Ignore errors
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Check if daemon process is running
115
+ * @returns {boolean} True if daemon is running
116
+ */
117
+ export function isDaemonRunning() {
118
+ const pid = getDaemonPid();
119
+ if (!pid) return false;
120
+
121
+ try {
122
+ // Send signal 0 to check if process exists
123
+ process.kill(pid, 0);
124
+ return true;
125
+ } catch (error) {
126
+ // Process doesn't exist
127
+ return false;
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Acquire daemon lock
133
+ * @returns {boolean} True if lock acquired
134
+ */
135
+ export function acquireLock() {
136
+ ensureDaemonDir();
137
+
138
+ try {
139
+ // Check if lock file exists and if process is still running
140
+ if (fs.existsSync(LOCK_FILE)) {
141
+ const lockData = JSON.parse(fs.readFileSync(LOCK_FILE, 'utf8'));
142
+ try {
143
+ process.kill(lockData.pid, 0);
144
+ // Process still running, cannot acquire lock
145
+ return false;
146
+ } catch (e) {
147
+ // Process not running, remove stale lock
148
+ fs.unlinkSync(LOCK_FILE);
149
+ }
150
+ }
151
+
152
+ // Create lock file
153
+ fs.writeFileSync(
154
+ LOCK_FILE,
155
+ JSON.stringify({
156
+ pid: process.pid,
157
+ createdAt: new Date().toISOString(),
158
+ }),
159
+ { mode: 0o600, flag: 'wx' }
160
+ );
161
+ return true;
162
+ } catch (error) {
163
+ if (error.code === 'EEXIST') {
164
+ // Lock file was created between our check and write
165
+ return false;
166
+ }
167
+ throw error;
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Release daemon lock
173
+ */
174
+ export function releaseLock() {
175
+ try {
176
+ if (fs.existsSync(LOCK_FILE)) {
177
+ const lockData = JSON.parse(fs.readFileSync(LOCK_FILE, 'utf8'));
178
+ // Only remove if we own the lock
179
+ if (lockData.pid === process.pid) {
180
+ fs.unlinkSync(LOCK_FILE);
181
+ }
182
+ }
183
+ } catch (error) {
184
+ // Ignore errors
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Get state file path (for external consumers like DevApp)
190
+ * @returns {string} Path to state file
191
+ */
192
+ export function getStateFilePath() {
193
+ return STATE_FILE;
194
+ }
195
+
196
+ /**
197
+ * Get daemon directory path
198
+ * @returns {string} Path to daemon directory
199
+ */
200
+ export function getDaemonDir() {
201
+ return DAEMON_DIR;
202
+ }
203
+
204
+ export default {
205
+ getDaemonState,
206
+ saveDaemonState,
207
+ updateDaemonState,
208
+ clearDaemonState,
209
+ getDaemonPid,
210
+ saveDaemonPid,
211
+ clearDaemonPid,
212
+ isDaemonRunning,
213
+ acquireLock,
214
+ releaseLock,
215
+ getStateFilePath,
216
+ getDaemonDir,
217
+ };
@@ -0,0 +1,107 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ import { encrypt, decrypt } from '../utils/encryption.js';
5
+
6
+ const FENWAVE_DIR = path.join(os.homedir(), '.fenwave');
7
+ const DEVICE_DIR = path.join(FENWAVE_DIR, 'device');
8
+ const CREDENTIAL_FILE = path.join(DEVICE_DIR, 'credential.json');
9
+
10
+ /**
11
+ * Ensure device directory exists
12
+ */
13
+ function ensureDeviceDir() {
14
+ if (!fs.existsSync(FENWAVE_DIR)) {
15
+ fs.mkdirSync(FENWAVE_DIR, { recursive: true, mode: 0o700 });
16
+ }
17
+ if (!fs.existsSync(DEVICE_DIR)) {
18
+ fs.mkdirSync(DEVICE_DIR, { recursive: true, mode: 0o700 });
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Save device credential to disk (encrypted)
24
+ *
25
+ * @param {Object} credentialData - Device credential data
26
+ * @param {string} credentialData.deviceId - Device ID
27
+ * @param {string} credentialData.deviceCredential - Device credential
28
+ * @param {string} credentialData.userEntityRef - User entity reference
29
+ * @param {string} credentialData.deviceName - Device name
30
+ * @param {string} credentialData.platform - Platform
31
+ * @param {string} credentialData.agentVersion - Agent version
32
+ */
33
+ export function saveDeviceCredential(credentialData) {
34
+ ensureDeviceDir();
35
+
36
+ const data = {
37
+ deviceId: credentialData.deviceId,
38
+ deviceCredential: encrypt(credentialData.deviceCredential), // Encrypt credential
39
+ userEntityRef: credentialData.userEntityRef,
40
+ deviceName: credentialData.deviceName,
41
+ platform: credentialData.platform,
42
+ agentVersion: credentialData.agentVersion,
43
+ registeredAt: new Date().toISOString(),
44
+ };
45
+
46
+ fs.writeFileSync(CREDENTIAL_FILE, JSON.stringify(data, null, 2), {
47
+ mode: 0o600,
48
+ });
49
+ }
50
+
51
+ /**
52
+ * Load device credential from disk
53
+ *
54
+ * @returns {Object|null} Device credential data or null if not found
55
+ */
56
+ export function loadDeviceCredential() {
57
+ if (!fs.existsSync(CREDENTIAL_FILE)) {
58
+ return null;
59
+ }
60
+
61
+ try {
62
+ const data = JSON.parse(fs.readFileSync(CREDENTIAL_FILE, 'utf8'));
63
+
64
+ // Decrypt the credential
65
+ const decryptedCredential = decrypt(data.deviceCredential);
66
+
67
+ return {
68
+ deviceId: data.deviceId,
69
+ deviceCredential: decryptedCredential,
70
+ userEntityRef: data.userEntityRef,
71
+ deviceName: data.deviceName,
72
+ platform: data.platform,
73
+ agentVersion: data.agentVersion,
74
+ registeredAt: data.registeredAt,
75
+ };
76
+ } catch (error) {
77
+ console.error('Failed to load device credential:', error.message);
78
+ return null;
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Check if device is registered
84
+ *
85
+ * @returns {boolean} True if device credential exists
86
+ */
87
+ export function isDeviceRegistered() {
88
+ return fs.existsSync(CREDENTIAL_FILE);
89
+ }
90
+
91
+ /**
92
+ * Clear device credential (delete file)
93
+ */
94
+ export function clearDeviceCredential() {
95
+ if (fs.existsSync(CREDENTIAL_FILE)) {
96
+ fs.unlinkSync(CREDENTIAL_FILE);
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Get device credential file path
102
+ *
103
+ * @returns {string} Path to credential file
104
+ */
105
+ export function getCredentialFilePath() {
106
+ return CREDENTIAL_FILE;
107
+ }
@@ -0,0 +1,65 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+
5
+ const FENWAVE_DIR = path.join(os.homedir(), '.fenwave');
6
+ const NPM_DIR = path.join(FENWAVE_DIR, 'npm');
7
+ const NPM_TOKEN_FILE = path.join(NPM_DIR, 'token');
8
+
9
+ /**
10
+ * Ensure NPM directory exists
11
+ */
12
+ function ensureNpmDir() {
13
+ if (!fs.existsSync(FENWAVE_DIR)) {
14
+ fs.mkdirSync(FENWAVE_DIR, { recursive: true, mode: 0o700 });
15
+ }
16
+ if (!fs.existsSync(NPM_DIR)) {
17
+ fs.mkdirSync(NPM_DIR, { recursive: true, mode: 0o700 });
18
+ }
19
+ }
20
+
21
+ /**
22
+ * Save NPM token to disk
23
+ *
24
+ * @param {string} token - NPM token
25
+ */
26
+ export function saveNpmToken(token) {
27
+ ensureNpmDir();
28
+ fs.writeFileSync(NPM_TOKEN_FILE, token, { mode: 0o600 });
29
+ }
30
+
31
+ /**
32
+ * Load NPM token from disk
33
+ *
34
+ * @returns {string|null} NPM token or null if not found
35
+ */
36
+ export function loadNpmToken() {
37
+ if (!fs.existsSync(NPM_TOKEN_FILE)) {
38
+ return null;
39
+ }
40
+
41
+ try {
42
+ return fs.readFileSync(NPM_TOKEN_FILE, 'utf8').trim();
43
+ } catch (error) {
44
+ console.error('Failed to load NPM token:', error.message);
45
+ return null;
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Check if NPM token exists
51
+ *
52
+ * @returns {boolean} True if NPM token is configured
53
+ */
54
+ export function hasNpmToken() {
55
+ return fs.existsSync(NPM_TOKEN_FILE);
56
+ }
57
+
58
+ /**
59
+ * Clear NPM token (delete file)
60
+ */
61
+ export function clearNpmToken() {
62
+ if (fs.existsSync(NPM_TOKEN_FILE)) {
63
+ fs.unlinkSync(NPM_TOKEN_FILE);
64
+ }
65
+ }