envseed 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/Dockerfile.simulation +18 -0
- package/README.md +498 -0
- package/bin/dashboard.mjs +706 -0
- package/bin/propensity-monitor.mjs +897 -0
- package/commands/log-incident.md +20 -0
- package/entrypoint.sh +93 -0
- package/lib/background-analyzer.mjs +113 -0
- package/lib/container-replicator.mjs +690 -0
- package/lib/hook-handler.mjs +109 -0
- package/lib/llm-analyzer.mjs +247 -0
- package/lib/log-incident.mjs +320 -0
- package/lib/logger.mjs +42 -0
- package/lib/personas.mjs +176 -0
- package/lib/redaction-review.mjs +255 -0
- package/lib/risk-analyzer.mjs +477 -0
- package/lib/s3.mjs +191 -0
- package/lib/session-tracker.mjs +132 -0
- package/lib/simulation-orchestrator.mjs +492 -0
- package/lib/utils.mjs +33 -0
- package/package.json +28 -0
- package/postinstall.mjs +165 -0
package/postinstall.mjs
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* envseed postinstall — sets up hooks and config after npm install.
|
|
5
|
+
* Node.js rewrite of install.sh for cross-platform compatibility.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
|
|
12
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const HOME = process.env.HOME || process.env.USERPROFILE;
|
|
14
|
+
const INSTALL_DIR = path.join(HOME, '.propensity-monitor');
|
|
15
|
+
const CLAUDE_SETTINGS = path.join(HOME, '.claude', 'settings.json');
|
|
16
|
+
const COMMANDS_DIR = path.join(HOME, '.claude', 'commands');
|
|
17
|
+
|
|
18
|
+
// Default config — uploadEndpoint is filled after deploy
|
|
19
|
+
const DEFAULT_CONFIG = {
|
|
20
|
+
enabled: true,
|
|
21
|
+
alertThreshold: 3,
|
|
22
|
+
logAllEvents: true,
|
|
23
|
+
maxLogSizeMB: 500,
|
|
24
|
+
s3Bucket: 'metr-propensity-monitor',
|
|
25
|
+
s3Region: 'us-east-1',
|
|
26
|
+
s3Profile: '',
|
|
27
|
+
uploadEndpoint: 'https://envseed-api.sydv793.workers.dev',
|
|
28
|
+
githubClientId: 'Ov23lid2fKxyN7lOd9qv',
|
|
29
|
+
apiKey: '',
|
|
30
|
+
simulationCount: 2,
|
|
31
|
+
simulationMaxTurns: 100,
|
|
32
|
+
simulationConcurrency: 5,
|
|
33
|
+
simulationModels: ['claude-haiku-4-5-20251001'],
|
|
34
|
+
enableSimulations: true,
|
|
35
|
+
replicaModel: 'claude-opus-4-6',
|
|
36
|
+
redactionReview: true,
|
|
37
|
+
proxyUrl: '',
|
|
38
|
+
proxyToken: '',
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
function mkdirp(dir) {
|
|
42
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function copyDir(src, dest) {
|
|
46
|
+
mkdirp(dest);
|
|
47
|
+
for (const entry of fs.readdirSync(src)) {
|
|
48
|
+
const srcPath = path.join(src, entry);
|
|
49
|
+
const destPath = path.join(dest, entry);
|
|
50
|
+
if (fs.statSync(srcPath).isFile()) {
|
|
51
|
+
fs.copyFileSync(srcPath, destPath);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function readJson(filePath) {
|
|
57
|
+
try { return JSON.parse(fs.readFileSync(filePath, 'utf8')); } catch { return null; }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function writeJson(filePath, data) {
|
|
61
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
console.log('Planting envseed...');
|
|
66
|
+
|
|
67
|
+
// 1. Create directory structure
|
|
68
|
+
for (const sub of ['bin', 'lib', 'data/events', 'data/sessions', 'data/alerts', 'data/incidents', 'data/replicas']) {
|
|
69
|
+
mkdirp(path.join(INSTALL_DIR, sub));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 2. Copy lib and bin files from npm package
|
|
73
|
+
copyDir(path.join(__dirname, 'lib'), path.join(INSTALL_DIR, 'lib'));
|
|
74
|
+
copyDir(path.join(__dirname, 'bin'), path.join(INSTALL_DIR, 'bin'));
|
|
75
|
+
|
|
76
|
+
// Copy Dockerfile and entrypoint if present
|
|
77
|
+
for (const f of ['Dockerfile.simulation', 'entrypoint.sh']) {
|
|
78
|
+
const src = path.join(__dirname, f);
|
|
79
|
+
if (fs.existsSync(src)) {
|
|
80
|
+
fs.copyFileSync(src, path.join(INSTALL_DIR, f));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Store source dir for later sync
|
|
85
|
+
fs.writeFileSync(path.join(INSTALL_DIR, '.source-dir'), __dirname + '\n');
|
|
86
|
+
|
|
87
|
+
// 3. Make CLI executable
|
|
88
|
+
try {
|
|
89
|
+
fs.chmodSync(path.join(INSTALL_DIR, 'bin', 'propensity-monitor.mjs'), 0o755);
|
|
90
|
+
} catch {}
|
|
91
|
+
|
|
92
|
+
// 4. Install slash command
|
|
93
|
+
mkdirp(COMMANDS_DIR);
|
|
94
|
+
const cmdSrc = path.join(__dirname, 'commands', 'log-incident.md');
|
|
95
|
+
if (fs.existsSync(cmdSrc)) {
|
|
96
|
+
fs.copyFileSync(cmdSrc, path.join(COMMANDS_DIR, 'log-incident.md'));
|
|
97
|
+
console.log(' Slash command planted: /log-incident');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 5. Create or merge config
|
|
101
|
+
const configPath = path.join(INSTALL_DIR, 'config.json');
|
|
102
|
+
let config = readJson(configPath) || {};
|
|
103
|
+
let added = 0;
|
|
104
|
+
for (const [key, value] of Object.entries(DEFAULT_CONFIG)) {
|
|
105
|
+
if (!(key in config)) {
|
|
106
|
+
config[key] = value;
|
|
107
|
+
added++;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
writeJson(configPath, config);
|
|
111
|
+
if (added > 0) console.log(` Added ${added} config key(s)`);
|
|
112
|
+
|
|
113
|
+
// 6. Initialize index
|
|
114
|
+
const indexPath = path.join(INSTALL_DIR, 'data', 'index.json');
|
|
115
|
+
if (!fs.existsSync(indexPath)) {
|
|
116
|
+
writeJson(indexPath, {});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 7. Register hooks in Claude settings
|
|
120
|
+
mkdirp(path.dirname(CLAUDE_SETTINGS));
|
|
121
|
+
let settings = readJson(CLAUDE_SETTINGS) || {};
|
|
122
|
+
if (!settings.hooks) settings.hooks = {};
|
|
123
|
+
|
|
124
|
+
const handlerCmd = `node ${INSTALL_DIR}/lib/hook-handler.mjs`;
|
|
125
|
+
const hookEntry = { type: 'command', command: handlerCmd, timeout: 15 };
|
|
126
|
+
const events = ['PreToolUse', 'PostToolUse', 'UserPromptSubmit', 'SessionStart', 'Stop'];
|
|
127
|
+
let hooksAdded = 0;
|
|
128
|
+
|
|
129
|
+
for (const event of events) {
|
|
130
|
+
if (!settings.hooks[event]) settings.hooks[event] = [];
|
|
131
|
+
|
|
132
|
+
// Remove old flat entries
|
|
133
|
+
settings.hooks[event] = settings.hooks[event].filter(entry => {
|
|
134
|
+
if (entry.command && entry.command.includes('propensity-monitor') && !entry.hooks) return false;
|
|
135
|
+
return true;
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Check if already installed
|
|
139
|
+
const already = settings.hooks[event].some(entry => {
|
|
140
|
+
if (entry.hooks) return entry.hooks.some(h => h.command && h.command.includes('propensity-monitor'));
|
|
141
|
+
return false;
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
if (!already) {
|
|
145
|
+
settings.hooks[event].push({ hooks: [{ ...hookEntry }] });
|
|
146
|
+
hooksAdded++;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
writeJson(CLAUDE_SETTINGS, settings);
|
|
151
|
+
if (hooksAdded > 0) console.log(` ${hooksAdded} hooks registered`);
|
|
152
|
+
|
|
153
|
+
console.log('');
|
|
154
|
+
console.log('envseed planted successfully!');
|
|
155
|
+
console.log('');
|
|
156
|
+
console.log(' Next steps:');
|
|
157
|
+
console.log(' 1. Run: envseed register');
|
|
158
|
+
console.log(' 2. Restart Claude Code');
|
|
159
|
+
console.log('');
|
|
160
|
+
console.log(' Run "envseed status" to check health.');
|
|
161
|
+
|
|
162
|
+
} catch (err) {
|
|
163
|
+
// Don't fail the npm install if postinstall has issues
|
|
164
|
+
console.error('envseed postinstall warning:', err.message);
|
|
165
|
+
}
|