filemayor 2.1.0 → 4.0.5
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 +90 -90
- package/README.md +96 -96
- package/core/ai/planner.js +42 -0
- package/core/ai/sentry.js +92 -0
- package/core/ai/strategist.js +186 -0
- package/core/ai/validator.js +116 -0
- package/core/analyzer.js +163 -152
- package/core/categories.js +235 -235
- package/core/cleaner.js +527 -527
- package/core/config.js +562 -562
- package/core/emergency-halt.js +104 -0
- package/core/engine/apply-engine.js +69 -0
- package/core/engine/cure-engine.js +70 -0
- package/core/engine/dedupe-engine.js +77 -0
- package/core/engine/explain-engine.js +114 -0
- package/core/engine/preview-engine.js +49 -0
- package/core/fs-abstraction.js +271 -0
- package/core/guardrail.js +115 -0
- package/core/index.js +135 -79
- package/core/intent-interpreter.js +301 -0
- package/core/jailer.js +151 -0
- package/core/license.js +403 -337
- package/core/metadata-store.js +104 -0
- package/core/organizer.js +414 -528
- package/core/reporter.js +783 -653
- package/core/scanner.js +466 -436
- package/core/security.js +370 -317
- package/core/sop-parser.js +564 -564
- package/core/telemetry.js +74 -0
- package/core/vault.js +179 -0
- package/core/watcher.js +478 -478
- package/index.js +895 -685
- package/package.json +3 -3
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
|
|
7
|
+
const PING_URL = 'https://filemayor.com/api/ping';
|
|
8
|
+
const STATE_FILE = path.join(os.homedir(), '.filemayor-telemetry.json');
|
|
9
|
+
|
|
10
|
+
// Fire-and-forget anonymous ping.
|
|
11
|
+
// Sends: version, OS platform, CPU arch, and top-level command name.
|
|
12
|
+
// Never sends: file names, paths, sizes, contents, or any PII.
|
|
13
|
+
// Opt-out: set FILEMAYOR_NO_TELEMETRY=1 in environment.
|
|
14
|
+
// Fires at most once per version (tracked in ~/.filemayor-telemetry.json).
|
|
15
|
+
function ping(version, cmd) {
|
|
16
|
+
if (process.env.FILEMAYOR_NO_TELEMETRY === '1') return;
|
|
17
|
+
if (process.env.CI) return;
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const state = readState();
|
|
21
|
+
if (state.pinged && state.pinged.includes(version)) return;
|
|
22
|
+
|
|
23
|
+
const payload = JSON.stringify({
|
|
24
|
+
v: version,
|
|
25
|
+
os: os.platform(),
|
|
26
|
+
arch: os.arch(),
|
|
27
|
+
cmd: cmd || 'unknown',
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Fire over https using built-in https module — no extra deps
|
|
31
|
+
const https = require('https');
|
|
32
|
+
const url = new URL(PING_URL);
|
|
33
|
+
const req = https.request({
|
|
34
|
+
hostname: url.hostname,
|
|
35
|
+
path: url.pathname,
|
|
36
|
+
method: 'POST',
|
|
37
|
+
headers: {
|
|
38
|
+
'Content-Type': 'application/json',
|
|
39
|
+
'Content-Length': Buffer.byteLength(payload),
|
|
40
|
+
'User-Agent': `filemayor-cli/${version}`,
|
|
41
|
+
},
|
|
42
|
+
timeout: 3000,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
req.on('error', () => {}); // swallow all errors — telemetry must never crash the CLI
|
|
46
|
+
req.on('timeout', () => req.destroy());
|
|
47
|
+
req.end(payload);
|
|
48
|
+
|
|
49
|
+
markPinged(state, version);
|
|
50
|
+
} catch {
|
|
51
|
+
// intentionally silent
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function readState() {
|
|
56
|
+
try {
|
|
57
|
+
return JSON.parse(fs.readFileSync(STATE_FILE, 'utf-8'));
|
|
58
|
+
} catch {
|
|
59
|
+
return { pinged: [] };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function markPinged(state, version) {
|
|
64
|
+
try {
|
|
65
|
+
const pinged = Array.isArray(state.pinged) ? state.pinged : [];
|
|
66
|
+
pinged.push(version);
|
|
67
|
+
// Keep only last 20 versions to bound file size
|
|
68
|
+
fs.writeFileSync(STATE_FILE, JSON.stringify({ pinged: pinged.slice(-20) }), 'utf-8');
|
|
69
|
+
} catch {
|
|
70
|
+
// intentionally silent
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
module.exports = { ping };
|
package/core/vault.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ═══════════════════════════════════════════════════════════════════
|
|
5
|
+
* FILEMAYOR CORE — VAULT (ZERO-DEPENDENCY SECRET MANAGER)
|
|
6
|
+
*
|
|
7
|
+
* Uses the OS-native credential store to keep API keys out of
|
|
8
|
+
* the filesystem entirely. Falls back to environment variables only.
|
|
9
|
+
*
|
|
10
|
+
* Supported backends:
|
|
11
|
+
* macOS → security (Keychain Services)
|
|
12
|
+
* Windows → PowerShell CredentialManager
|
|
13
|
+
* Linux → secret-tool (freedesktop Secret Service)
|
|
14
|
+
* ═══════════════════════════════════════════════════════════════════
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
'use strict';
|
|
18
|
+
|
|
19
|
+
const { execFileSync } = require('child_process');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
|
|
23
|
+
const SERVICE_NAME = 'FileMayor';
|
|
24
|
+
|
|
25
|
+
// ─── Platform-Specific Implementations ───────────────────────────
|
|
26
|
+
|
|
27
|
+
function _saveSecret_darwin(key, value) {
|
|
28
|
+
execFileSync('security', [
|
|
29
|
+
'add-generic-password',
|
|
30
|
+
'-a', SERVICE_NAME,
|
|
31
|
+
'-s', key,
|
|
32
|
+
'-w', value,
|
|
33
|
+
'-U',
|
|
34
|
+
], { stdio: 'ignore' });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function _getSecret_darwin(key) {
|
|
38
|
+
return execFileSync('security', [
|
|
39
|
+
'find-generic-password',
|
|
40
|
+
'-a', SERVICE_NAME,
|
|
41
|
+
'-s', key,
|
|
42
|
+
'-w',
|
|
43
|
+
], { encoding: 'utf8' }).trim();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function _deleteSecret_darwin(key) {
|
|
47
|
+
execFileSync('security', [
|
|
48
|
+
'delete-generic-password',
|
|
49
|
+
'-a', SERVICE_NAME,
|
|
50
|
+
'-s', key,
|
|
51
|
+
], { stdio: 'ignore' });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function _saveSecret_win32(key, value) {
|
|
55
|
+
// Pass the secret value via an environment variable to avoid it
|
|
56
|
+
// appearing in the command string or the process table.
|
|
57
|
+
const scriptBlock = `
|
|
58
|
+
$target = "${SERVICE_NAME}_${key.replace(/[^A-Za-z0-9_-]/g, '_')}"
|
|
59
|
+
$user = "${key.replace(/[^A-Za-z0-9_-]/g, '_')}"
|
|
60
|
+
$pass = $env:FM_VAULT_VALUE
|
|
61
|
+
cmdkey /generic:$target /user:$user /pass:$pass | Out-Null
|
|
62
|
+
`.trim();
|
|
63
|
+
execFileSync('powershell', [
|
|
64
|
+
'-NoProfile', '-NonInteractive', '-Command', scriptBlock,
|
|
65
|
+
], {
|
|
66
|
+
stdio: 'ignore',
|
|
67
|
+
env: { ...process.env, FM_VAULT_VALUE: value },
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function _getSecret_win32(key) {
|
|
72
|
+
const safeKey = key.replace(/[^A-Za-z0-9_-]/g, '_');
|
|
73
|
+
const target = `${SERVICE_NAME}_${safeKey}`;
|
|
74
|
+
const scriptBlock =
|
|
75
|
+
`$c = Get-StoredCredential -Target '${target}'; ` +
|
|
76
|
+
`if ($c) { $c.GetNetworkCredential().Password } else { exit 1 }`;
|
|
77
|
+
try {
|
|
78
|
+
return execFileSync('powershell', [
|
|
79
|
+
'-NoProfile', '-NonInteractive', '-Command', scriptBlock,
|
|
80
|
+
], { encoding: 'utf8' }).trim();
|
|
81
|
+
} catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function _saveSecret_linux(key, value) {
|
|
87
|
+
// Pass the secret via stdin so it never appears in the process table.
|
|
88
|
+
execFileSync('secret-tool', [
|
|
89
|
+
'store',
|
|
90
|
+
'--label', `${SERVICE_NAME} ${key}`,
|
|
91
|
+
'service', SERVICE_NAME,
|
|
92
|
+
'key', key,
|
|
93
|
+
], { input: value + '\n', stdio: ['pipe', 'ignore', 'ignore'] });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function _getSecret_linux(key) {
|
|
97
|
+
return execFileSync('secret-tool', [
|
|
98
|
+
'lookup',
|
|
99
|
+
'service', SERVICE_NAME,
|
|
100
|
+
'key', key,
|
|
101
|
+
], { encoding: 'utf8' }).trim();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ─── The Vault ────────────────────────────────────────────────────
|
|
105
|
+
|
|
106
|
+
const Vault = {
|
|
107
|
+
/**
|
|
108
|
+
* Save a secret to the system keychain.
|
|
109
|
+
* @param {string} key Secret name (e.g. 'GEMINI_API_KEY')
|
|
110
|
+
* @param {string} value Secret value
|
|
111
|
+
* @returns {boolean} true on success
|
|
112
|
+
*/
|
|
113
|
+
saveSecret(key, value) {
|
|
114
|
+
try {
|
|
115
|
+
if (process.platform === 'darwin') _saveSecret_darwin(key, value);
|
|
116
|
+
else if (process.platform === 'win32') _saveSecret_win32(key, value);
|
|
117
|
+
else _saveSecret_linux(key, value);
|
|
118
|
+
return true;
|
|
119
|
+
} catch (err) {
|
|
120
|
+
console.warn(`[Vault] Could not save to system keychain: ${err.message}`);
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Retrieve a secret — checks System Keychain → ENV.
|
|
127
|
+
* The .env plaintext fallback has been removed. Set secrets via
|
|
128
|
+
* `filemayor config set KEY value` (stores in keychain) or
|
|
129
|
+
* export them as shell environment variables before starting.
|
|
130
|
+
* @param {string} key Secret name
|
|
131
|
+
* @returns {string|null}
|
|
132
|
+
*/
|
|
133
|
+
getSecret(key) {
|
|
134
|
+
// Priority 1: System Keychain (most secure)
|
|
135
|
+
try {
|
|
136
|
+
if (process.platform === 'darwin') return _getSecret_darwin(key);
|
|
137
|
+
else if (process.platform === 'win32') {
|
|
138
|
+
const val = _getSecret_win32(key);
|
|
139
|
+
if (val) return val;
|
|
140
|
+
}
|
|
141
|
+
else return _getSecret_linux(key);
|
|
142
|
+
} catch { /* Keychain not available or key not found */ }
|
|
143
|
+
|
|
144
|
+
// Priority 2: Environment Variable
|
|
145
|
+
if (process.env[key]) return process.env[key];
|
|
146
|
+
|
|
147
|
+
return null;
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Delete a secret from the system keychain.
|
|
152
|
+
* @param {string} key Secret name
|
|
153
|
+
* @returns {boolean} true on success
|
|
154
|
+
*/
|
|
155
|
+
deleteSecret(key) {
|
|
156
|
+
try {
|
|
157
|
+
if (process.platform === 'darwin') _deleteSecret_darwin(key);
|
|
158
|
+
else if (process.platform === 'linux') {
|
|
159
|
+
execFileSync('secret-tool', [
|
|
160
|
+
'clear', 'service', SERVICE_NAME, 'key', key,
|
|
161
|
+
], { stdio: 'ignore' });
|
|
162
|
+
}
|
|
163
|
+
return true;
|
|
164
|
+
} catch {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Check if a secret exists anywhere in the priority chain.
|
|
171
|
+
* @param {string} key
|
|
172
|
+
* @returns {boolean}
|
|
173
|
+
*/
|
|
174
|
+
hasSecret(key) {
|
|
175
|
+
return Vault.getSecret(key) !== null;
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
module.exports = Vault;
|