fnos-cli 0.1.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.
- package/LICENSE +201 -0
- package/README.md +300 -0
- package/bin/fnos +3 -0
- package/package.json +3 -0
- package/src/commands/auth.js +61 -0
- package/src/commands/index.js +123 -0
- package/src/constants.js +69 -0
- package/src/index.js +57 -0
- package/src/utils/client.js +119 -0
- package/src/utils/formatter.js +366 -0
- package/src/utils/logger.js +120 -0
- package/src/utils/settings.js +106 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger utility using Winston
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const winston = require('winston');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const crypto = require('crypto');
|
|
9
|
+
const util = require('util');
|
|
10
|
+
|
|
11
|
+
// Create logs directory if it doesn't exist
|
|
12
|
+
const logsDir = path.join(os.homedir(), '.fnos', 'logs');
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
if (!fs.existsSync(logsDir)) {
|
|
15
|
+
fs.mkdirSync(logsDir, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Generate random number for log filename
|
|
19
|
+
const randomNum = crypto.randomBytes(4).toString('hex');
|
|
20
|
+
const logFilename = `fnos-${new Date().toISOString().split('T')[0]}-${randomNum}.log`;
|
|
21
|
+
|
|
22
|
+
// Log format
|
|
23
|
+
const logFormat = winston.format.printf(({ timestamp, level, message }) => {
|
|
24
|
+
return `${timestamp} - ${level.toUpperCase()} - ${message}`;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Create a custom transport that outputs to stderr
|
|
28
|
+
class StderrTransport extends winston.Transport {
|
|
29
|
+
constructor(opts = {}) {
|
|
30
|
+
super(opts);
|
|
31
|
+
this.level = opts.level || 'error';
|
|
32
|
+
this.format = opts.format || winston.format.simple();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
log(info, callback) {
|
|
36
|
+
setImmediate(() => {
|
|
37
|
+
this.emit('logged', info);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Format the message
|
|
41
|
+
const formatted = this.format.transform(info);
|
|
42
|
+
const message = typeof formatted === 'string' ? formatted : formatted[Symbol.for('message')] || formatted.message;
|
|
43
|
+
|
|
44
|
+
// Output to stderr
|
|
45
|
+
process.stderr.write(message + '\n');
|
|
46
|
+
|
|
47
|
+
callback();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Create logger
|
|
52
|
+
const logger = winston.createLogger({
|
|
53
|
+
level: 'silly', // Capture all levels for file output
|
|
54
|
+
format: winston.format.combine(
|
|
55
|
+
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
|
56
|
+
logFormat
|
|
57
|
+
),
|
|
58
|
+
transports: [
|
|
59
|
+
// Console transport - output all logs to stderr
|
|
60
|
+
new StderrTransport({
|
|
61
|
+
level: 'error', // Default: only show errors
|
|
62
|
+
format: winston.format.combine(
|
|
63
|
+
winston.format.colorize(),
|
|
64
|
+
logFormat
|
|
65
|
+
)
|
|
66
|
+
}),
|
|
67
|
+
// File transport - output all levels
|
|
68
|
+
new winston.transports.File({
|
|
69
|
+
level: 'silly', // Write all levels to file
|
|
70
|
+
filename: path.join(logsDir, logFilename),
|
|
71
|
+
maxsize: 10 * 1024 * 1024, // 10MB
|
|
72
|
+
maxFiles: 5
|
|
73
|
+
})
|
|
74
|
+
]
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Set log level based on verbose flag
|
|
79
|
+
* @param {number} verbose - Verbose level (0, 1, 2, 3)
|
|
80
|
+
*/
|
|
81
|
+
function setLogLevel(verbose) {
|
|
82
|
+
const consoleTransport = logger.transports.find(t => t instanceof StderrTransport);
|
|
83
|
+
|
|
84
|
+
if (verbose === 0) {
|
|
85
|
+
// Default: only show errors on console
|
|
86
|
+
consoleTransport.level = 'error';
|
|
87
|
+
} else if (verbose >= 1 && verbose <= 3) {
|
|
88
|
+
const levels = ['info', 'debug', 'silly'];
|
|
89
|
+
// Show logs at specified level on console
|
|
90
|
+
consoleTransport.level = levels[verbose - 1];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Also update the overall logger level to ensure file transport works correctly
|
|
94
|
+
logger.level = verbose >= 3 ? 'silly' : (verbose >= 2 ? 'debug' : (verbose >= 1 ? 'info' : 'error'));
|
|
95
|
+
|
|
96
|
+
// Try to update SDK logger level if available
|
|
97
|
+
try {
|
|
98
|
+
const fnosLogger = require('fnos/dist/logger');
|
|
99
|
+
if (fnosLogger && fnosLogger.default && fnosLogger.default.transports) {
|
|
100
|
+
// Remove existing console transport
|
|
101
|
+
const sdkConsoleTransport = fnosLogger.default.transports.find(t => t instanceof winston.transports.Console);
|
|
102
|
+
if (sdkConsoleTransport) {
|
|
103
|
+
fnosLogger.default.remove(sdkConsoleTransport);
|
|
104
|
+
}
|
|
105
|
+
// Add our custom stderr transport
|
|
106
|
+
const sdkStderrTransport = new StderrTransport({
|
|
107
|
+
level: consoleTransport.level,
|
|
108
|
+
format: winston.format.combine(
|
|
109
|
+
winston.format.colorize(),
|
|
110
|
+
logFormat
|
|
111
|
+
)
|
|
112
|
+
});
|
|
113
|
+
fnosLogger.default.add(sdkStderrTransport);
|
|
114
|
+
}
|
|
115
|
+
} catch (error) {
|
|
116
|
+
// Ignore if fnos logger is not available
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
module.exports = { logger, setLogLevel };
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings manager for fnos
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const os = require('os');
|
|
8
|
+
|
|
9
|
+
class Settings {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.settingsDir = path.join(os.homedir(), '.fnos');
|
|
12
|
+
this.settingsPath = path.join(this.settingsDir, 'settings.json');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Ensure settings directory exists
|
|
17
|
+
*/
|
|
18
|
+
_ensureDir() {
|
|
19
|
+
if (!fs.existsSync(this.settingsDir)) {
|
|
20
|
+
fs.mkdirSync(this.settingsDir, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Load settings from file
|
|
26
|
+
* @returns {Object|null} Settings object or null if not exists
|
|
27
|
+
*/
|
|
28
|
+
load() {
|
|
29
|
+
if (!fs.existsSync(this.settingsPath)) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const data = fs.readFileSync(this.settingsPath, 'utf8');
|
|
34
|
+
return JSON.parse(data);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error('Failed to load settings:', error.message);
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Save settings to file
|
|
43
|
+
* @param {Object} data - Settings data to save
|
|
44
|
+
*/
|
|
45
|
+
save(data) {
|
|
46
|
+
this._ensureDir();
|
|
47
|
+
try {
|
|
48
|
+
fs.writeFileSync(this.settingsPath, JSON.stringify(data, null, 2), {
|
|
49
|
+
mode: 0o600 // Read/write for owner only
|
|
50
|
+
});
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error('Failed to save settings:', error.message);
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Clear settings file
|
|
59
|
+
*/
|
|
60
|
+
clear() {
|
|
61
|
+
if (fs.existsSync(this.settingsPath)) {
|
|
62
|
+
fs.unlinkSync(this.settingsPath);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Check if settings file exists
|
|
68
|
+
* @returns {boolean} True if settings file exists
|
|
69
|
+
*/
|
|
70
|
+
exists() {
|
|
71
|
+
return fs.existsSync(this.settingsPath);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Get authentication credentials
|
|
76
|
+
* @returns {Object|null} Credentials object or null
|
|
77
|
+
*/
|
|
78
|
+
getCredentials() {
|
|
79
|
+
const settings = this.load();
|
|
80
|
+
if (!settings) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
endpoint: settings.endpoint,
|
|
85
|
+
username: settings.username,
|
|
86
|
+
password: settings.password,
|
|
87
|
+
token: settings.token,
|
|
88
|
+
longToken: settings.longToken,
|
|
89
|
+
secret: settings.secret
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Save authentication credentials
|
|
95
|
+
* @param {Object} credentials - Credentials to save
|
|
96
|
+
*/
|
|
97
|
+
saveCredentials(credentials) {
|
|
98
|
+
const existingSettings = this.load() || {};
|
|
99
|
+
this.save({
|
|
100
|
+
...existingSettings,
|
|
101
|
+
...credentials
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
module.exports = new Settings();
|