lucifer-gate 0.8.9 → 0.8.10-alpha.2.b3f7c1e
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/dist/server/cli/args.js +7 -0
- package/dist/server/cli/args.js.map +1 -0
- package/dist/server/cli/init_config.js +81 -0
- package/dist/server/cli/init_config.js.map +1 -0
- package/dist/server/cli/print_help.js +27 -0
- package/dist/server/cli/print_help.js.map +1 -0
- package/dist/server/cli/run_log.js +29 -0
- package/dist/server/cli/run_log.js.map +1 -0
- package/dist/server/cli/run_pair.js +61 -0
- package/dist/server/cli/run_pair.js.map +1 -0
- package/dist/server/cli/run_server.js +24 -0
- package/dist/server/cli/run_server.js.map +1 -0
- package/dist/server/cli/run_stats.js +48 -0
- package/dist/server/cli/run_stats.js.map +1 -0
- package/dist/server/cli.js +15 -273
- package/dist/server/cli.js.map +1 -1
- package/dist/server/domains/command-gateway/api/register_execute_routes.js +8 -168
- package/dist/server/domains/command-gateway/api/register_execute_routes.js.map +1 -1
- package/dist/server/domains/command-gateway/service/execute_and_audit.js +32 -0
- package/dist/server/domains/command-gateway/service/execute_and_audit.js.map +1 -0
- package/dist/server/domains/command-gateway/service/handle_manual_approval.js +122 -0
- package/dist/server/domains/command-gateway/service/handle_manual_approval.js.map +1 -0
- package/dist/server/domains/request-proxy/service/proxy_auth.js +126 -48
- package/dist/server/domains/request-proxy/service/proxy_auth.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../../../server/src/cli/args.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,WAAW,CAAC,IAAc,EAAE,IAAY;IACtD,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,GAAG,KAAK,CAAC,CAAC,IAAI,GAAG,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAC3D,OAAO,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;AACvB,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
2
|
+
import { resolve, join } from 'node:path';
|
|
3
|
+
import { generateApiKey, generateAdminSecret } from '../domains/command-gateway/repository/api_key_store.js';
|
|
4
|
+
export function initConfig(targetDir) {
|
|
5
|
+
const configDir = resolve(targetDir, 'config');
|
|
6
|
+
const dataDir = resolve(targetDir, 'data');
|
|
7
|
+
mkdirSync(configDir, { recursive: true });
|
|
8
|
+
mkdirSync(dataDir, { recursive: true });
|
|
9
|
+
const luciferJsonPath = join(configDir, 'lucifer.json');
|
|
10
|
+
const apiKeysPath = join(configDir, 'api-keys.json');
|
|
11
|
+
const commandRulesPath = join(configDir, 'command-rules.json');
|
|
12
|
+
const proxyConfigPath = join(configDir, 'proxy-config.json');
|
|
13
|
+
if (existsSync(luciferJsonPath)) {
|
|
14
|
+
console.log(`Config already exists: ${luciferJsonPath}`);
|
|
15
|
+
console.log('Delete it first if you want to regenerate.');
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const { key, salt, keyHash } = generateApiKey();
|
|
19
|
+
const { secret: adminSecret, salt: adminSalt, secretHash: adminHash } = generateAdminSecret();
|
|
20
|
+
writeFileSync(luciferJsonPath, JSON.stringify({
|
|
21
|
+
port: 3001,
|
|
22
|
+
adminSecretHash: adminHash,
|
|
23
|
+
adminSecretSalt: adminSalt,
|
|
24
|
+
approvalTimeoutSeconds: 300,
|
|
25
|
+
executionTimeoutSeconds: 120,
|
|
26
|
+
maxConcurrentExecutions: 5,
|
|
27
|
+
maxOutputBytes: 10485760,
|
|
28
|
+
rateLimitPerMinute: 10,
|
|
29
|
+
onApprovalTimeout: 'deny',
|
|
30
|
+
dataDir: '../data',
|
|
31
|
+
logFile: 'lucifer.log',
|
|
32
|
+
}, null, 2) + '\n');
|
|
33
|
+
writeFileSync(apiKeysPath, JSON.stringify({
|
|
34
|
+
keys: [{
|
|
35
|
+
id: crypto.randomUUID(),
|
|
36
|
+
name: 'default',
|
|
37
|
+
keyHash,
|
|
38
|
+
salt,
|
|
39
|
+
allowedIps: [],
|
|
40
|
+
createdAt: new Date().toISOString(),
|
|
41
|
+
active: true,
|
|
42
|
+
}],
|
|
43
|
+
}, null, 2) + '\n');
|
|
44
|
+
writeFileSync(commandRulesPath, JSON.stringify({
|
|
45
|
+
rules: [
|
|
46
|
+
{ prefix: 'echo ', action: 'always_approve' },
|
|
47
|
+
{ prefix: 'git status', action: 'always_approve' },
|
|
48
|
+
{ prefix: 'git pull', action: 'manual_approve' },
|
|
49
|
+
{ prefix: 'git push', action: 'manual_approve' },
|
|
50
|
+
{ prefix: 'npm test', action: 'always_approve' },
|
|
51
|
+
{ prefix: 'npm run', action: 'manual_approve' },
|
|
52
|
+
{ prefix: 'rm ', action: 'always_deny' },
|
|
53
|
+
],
|
|
54
|
+
defaultAction: 'always_deny',
|
|
55
|
+
}, null, 2) + '\n');
|
|
56
|
+
writeFileSync(proxyConfigPath, JSON.stringify({
|
|
57
|
+
proxies: [],
|
|
58
|
+
}, null, 2) + '\n');
|
|
59
|
+
console.log('Config files generated:');
|
|
60
|
+
console.log(` ${luciferJsonPath}`);
|
|
61
|
+
console.log(` ${apiKeysPath}`);
|
|
62
|
+
console.log(` ${commandRulesPath}`);
|
|
63
|
+
console.log(` ${proxyConfigPath}`);
|
|
64
|
+
console.log('');
|
|
65
|
+
console.log('Your API key (save this, it cannot be recovered):');
|
|
66
|
+
console.log(` ${key}`);
|
|
67
|
+
console.log('');
|
|
68
|
+
console.log('Your admin secret for the web approval UI (save this, it cannot be recovered):');
|
|
69
|
+
console.log(` ${adminSecret}`);
|
|
70
|
+
console.log('');
|
|
71
|
+
console.log('Quick start:');
|
|
72
|
+
console.log(' # Dev mode (no Telegram needed):');
|
|
73
|
+
console.log(` LUCIFER_TELEGRAM_TOKEN=skip lucifer-gate --config ${luciferJsonPath} --auto-approve`);
|
|
74
|
+
console.log('');
|
|
75
|
+
console.log(' # Production — pair your Telegram chat first:');
|
|
76
|
+
console.log(` LUCIFER_TELEGRAM_TOKEN=your_token lucifer-gate pair --config ${luciferJsonPath}`);
|
|
77
|
+
console.log('');
|
|
78
|
+
console.log(' # Then start the server:');
|
|
79
|
+
console.log(` LUCIFER_TELEGRAM_TOKEN=your_token lucifer-gate --config ${luciferJsonPath}`);
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=init_config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init_config.js","sourceRoot":"","sources":["../../../server/src/cli/init_config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function printHelp() {
|
|
2
|
+
console.log(`
|
|
3
|
+
lucifer-gate - AI Agent Command Firewall
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
lucifer-gate start [options] Start the server (explicit form)
|
|
7
|
+
lucifer-gate [options] Start the server (implicit, same as 'start')
|
|
8
|
+
lucifer-gate --init [dir] Generate starter config files
|
|
9
|
+
lucifer-gate pair [--config <path>] Pair a Telegram chat for approvals
|
|
10
|
+
lucifer-gate log [--limit N] Query audit log
|
|
11
|
+
lucifer-gate stats Show approval statistics
|
|
12
|
+
|
|
13
|
+
Server options:
|
|
14
|
+
--config <path> Path to lucifer.json (default: ./config/lucifer.json)
|
|
15
|
+
--port <number> Server port (default: 3001, or PORT env var)
|
|
16
|
+
--data-dir <path> Directory for SQLite database (default: ./data)
|
|
17
|
+
--auto-approve Auto-approve all commands (dev mode, no Telegram needed)
|
|
18
|
+
--help Show this help
|
|
19
|
+
|
|
20
|
+
Environment variables:
|
|
21
|
+
LUCIFER_TELEGRAM_TOKEN Telegram bot token (required for production)
|
|
22
|
+
LUCIFER_TELEGRAM_CHAT_ID Telegram chat ID for approvals (or use 'pair' command)
|
|
23
|
+
PORT Server port (default: 3001)
|
|
24
|
+
LOG_LEVEL Log level: debug, info, warn, error (default: debug / info in production)
|
|
25
|
+
`);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=print_help.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"print_help.js","sourceRoot":"","sources":["../../../server/src/cli/print_help.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,SAAS;IACvB,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;CAuBb,CAAC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { getDatabase } from '../domains/command-gateway/repository/database.js';
|
|
2
|
+
import { createAuditLog } from '../domains/command-gateway/repository/audit_log.js';
|
|
3
|
+
import { redactApiKeyName } from '../domains/command-gateway/service/redact_api_key_name.js';
|
|
4
|
+
export async function runLog(limit, dataDir) {
|
|
5
|
+
const db = getDatabase(dataDir);
|
|
6
|
+
const auditLog = createAuditLog(db);
|
|
7
|
+
const entries = auditLog.query(limit);
|
|
8
|
+
if (entries.length === 0) {
|
|
9
|
+
console.log('No audit log entries found.');
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const reversed = [...entries].reverse();
|
|
13
|
+
for (const entry of reversed) {
|
|
14
|
+
const time = entry.ts.replace('T', ' ').replace(/\.\d+Z$/, 'Z');
|
|
15
|
+
const parts = [time, entry.type.toUpperCase().padEnd(16)];
|
|
16
|
+
if (entry.command)
|
|
17
|
+
parts.push(entry.command);
|
|
18
|
+
if (entry.apiKeyName)
|
|
19
|
+
parts.push(`key=${redactApiKeyName(entry.apiKeyName)}`);
|
|
20
|
+
if (entry.exitCode !== undefined && entry.exitCode !== null)
|
|
21
|
+
parts.push(`exit=${entry.exitCode}`);
|
|
22
|
+
if (entry.durationMs !== undefined && entry.durationMs !== null)
|
|
23
|
+
parts.push(`${entry.durationMs}ms`);
|
|
24
|
+
if (entry.error)
|
|
25
|
+
parts.push(`error: ${entry.error}`);
|
|
26
|
+
console.log(parts.join(' '));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=run_log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run_log.js","sourceRoot":"","sources":["../../../server/src/cli/run_log.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mDAAmD,CAAC;AAChF,OAAO,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAC;AACpF,OAAO,EAAE,gBAAgB,EAAE,MAAM,2DAA2D,CAAC;AAE7F,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,KAAa,EAAE,OAAe;IACzD,MAAM,EAAE,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IAChC,MAAM,QAAQ,GAAG,cAAc,CAAC,EAAE,CAAC,CAAC;IACpC,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAEtC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;QAC3C,OAAO;IACT,CAAC;IAED,MAAM,QAAQ,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC;IACxC,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;QAChE,MAAM,KAAK,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QAC1D,IAAI,KAAK,CAAC,OAAO;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC7C,IAAI,KAAK,CAAC,UAAU;YAAE,KAAK,CAAC,IAAI,CAAC,OAAO,gBAAgB,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QAC9E,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;QAClG,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,UAAU,IAAI,CAAC,CAAC;QACrG,IAAI,KAAK,CAAC,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,UAAU,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;QACrD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAChC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { createInterface } from 'node:readline/promises';
|
|
3
|
+
import { runTelegramPairing } from '../domains/command-gateway/service/telegram_pairing.js';
|
|
4
|
+
import { getTelegramToken } from '../domains/command-gateway/config/gateway_config.js';
|
|
5
|
+
import { updateJsonConfig } from '../lib/json_config_writer.js';
|
|
6
|
+
function createReadlinePairingIO() {
|
|
7
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
8
|
+
return {
|
|
9
|
+
print(msg) {
|
|
10
|
+
console.log(msg);
|
|
11
|
+
},
|
|
12
|
+
async choose(prompt, options) {
|
|
13
|
+
console.log(`\n${prompt}`);
|
|
14
|
+
for (let i = 0; i < options.length; i++) {
|
|
15
|
+
console.log(` ${i + 1}. ${options[i]}`);
|
|
16
|
+
}
|
|
17
|
+
while (true) {
|
|
18
|
+
const answer = await rl.question(`\nEnter number (1-${options.length}): `);
|
|
19
|
+
const num = Number.parseInt(answer.trim(), 10);
|
|
20
|
+
if (num >= 1 && num <= options.length)
|
|
21
|
+
return num - 1;
|
|
22
|
+
console.log(`Please enter a number between 1 and ${options.length}.`);
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
async confirm(prompt) {
|
|
26
|
+
const answer = await rl.question(`${prompt} [y/N] `);
|
|
27
|
+
return /^y(es)?$/i.test(answer.trim());
|
|
28
|
+
},
|
|
29
|
+
async prompt(msg) {
|
|
30
|
+
return rl.question(`${msg} `);
|
|
31
|
+
},
|
|
32
|
+
close() {
|
|
33
|
+
rl.close();
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export async function runPair(configPath) {
|
|
38
|
+
const resolvedConfigPath = resolve(configPath);
|
|
39
|
+
let token;
|
|
40
|
+
try {
|
|
41
|
+
token = getTelegramToken();
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
console.error('LUCIFER_TELEGRAM_TOKEN environment variable is required for pairing.\n' +
|
|
45
|
+
'Create a bot via @BotFather on Telegram and set the token:\n\n' +
|
|
46
|
+
' LUCIFER_TELEGRAM_TOKEN=your_token lucifer-gate pair');
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
const io = createReadlinePairingIO();
|
|
50
|
+
try {
|
|
51
|
+
const result = await runTelegramPairing(token, io, { waitForChats: true });
|
|
52
|
+
updateJsonConfig(resolvedConfigPath, { telegramChatId: result.chatId });
|
|
53
|
+
console.log(`\nChat ID ${result.chatId} saved to ${resolvedConfigPath}`);
|
|
54
|
+
console.log('You can now start Lucifer without LUCIFER_TELEGRAM_CHAT_ID:');
|
|
55
|
+
console.log(`\n LUCIFER_TELEGRAM_TOKEN=your_token lucifer-gate start --config ${resolvedConfigPath}`);
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
io.close();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=run_pair.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run_pair.js","sourceRoot":"","sources":["../../../server/src/cli/run_pair.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createApp } from '../create_app.js';
|
|
2
|
+
import { logger } from '../lib/logger.js';
|
|
3
|
+
export async function runServer(options) {
|
|
4
|
+
if (options.port) {
|
|
5
|
+
process.env.PORT = options.port;
|
|
6
|
+
}
|
|
7
|
+
const { app, config, start, stop } = createApp({
|
|
8
|
+
configPath: options.configPath,
|
|
9
|
+
autoApprove: options.autoApprove,
|
|
10
|
+
});
|
|
11
|
+
const server = app.listen(config.port, async () => {
|
|
12
|
+
logger.info({ port: config.port, autoApprove: options.autoApprove }, 'Lucifer listening');
|
|
13
|
+
await start();
|
|
14
|
+
});
|
|
15
|
+
const shutdown = async () => {
|
|
16
|
+
logger.info('Shutting down');
|
|
17
|
+
await stop();
|
|
18
|
+
server.close();
|
|
19
|
+
process.exit(0);
|
|
20
|
+
};
|
|
21
|
+
process.on('SIGTERM', shutdown);
|
|
22
|
+
process.on('SIGINT', shutdown);
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=run_server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run_server.js","sourceRoot":"","sources":["../../../server/src/cli/run_server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAQ1C,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAyB;IACvD,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAClC,CAAC;IAED,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,SAAS,CAAC;QAC7C,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,WAAW,EAAE,OAAO,CAAC,WAAW;KACjC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;QAChD,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,EAAE,mBAAmB,CAAC,CAAC;QAC1F,MAAM,KAAK,EAAE,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;QAC1B,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC7B,MAAM,IAAI,EAAE,CAAC;QACb,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IAEF,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AACjC,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { getDatabase } from '../domains/command-gateway/repository/database.js';
|
|
2
|
+
import { createAuditLog } from '../domains/command-gateway/repository/audit_log.js';
|
|
3
|
+
import { createApprovalStore } from '../domains/command-gateway/repository/approval_store.js';
|
|
4
|
+
export async function runStats(dataDir) {
|
|
5
|
+
const db = getDatabase(dataDir);
|
|
6
|
+
const auditLog = createAuditLog(db);
|
|
7
|
+
const approvalStore = createApprovalStore(db);
|
|
8
|
+
const allEntries = auditLog.query(10000);
|
|
9
|
+
const requests = allEntries.filter(e => e.type === 'request');
|
|
10
|
+
const approvals = allEntries.filter(e => e.type === 'approved');
|
|
11
|
+
const denials = allEntries.filter(e => e.type === 'denied');
|
|
12
|
+
const executions = allEntries.filter(e => e.type === 'executed');
|
|
13
|
+
const activeApprovals = approvalStore.listAll(10000);
|
|
14
|
+
const expired = activeApprovals.filter(a => a.expiresAt && new Date(a.expiresAt) < new Date());
|
|
15
|
+
console.log('Lucifer Stats');
|
|
16
|
+
console.log('=============');
|
|
17
|
+
console.log(`Total requests: ${requests.length}`);
|
|
18
|
+
console.log(`Approved: ${approvals.length}`);
|
|
19
|
+
console.log(`Denied: ${denials.length}`);
|
|
20
|
+
console.log(`Executed: ${executions.length}`);
|
|
21
|
+
console.log(`Active approvals: ${activeApprovals.length - expired.length}`);
|
|
22
|
+
console.log(`Expired approvals: ${expired.length}`);
|
|
23
|
+
if (executions.length > 0) {
|
|
24
|
+
const durations = executions.filter(e => e.durationMs != null).map(e => e.durationMs);
|
|
25
|
+
if (durations.length > 0) {
|
|
26
|
+
const avg = Math.round(durations.reduce((a, b) => a + b, 0) / durations.length);
|
|
27
|
+
const max = Math.max(...durations);
|
|
28
|
+
console.log(`Avg exec time: ${avg}ms`);
|
|
29
|
+
console.log(`Max exec time: ${max}ms`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Top commands
|
|
33
|
+
const cmdCounts = new Map();
|
|
34
|
+
for (const r of requests) {
|
|
35
|
+
if (r.command) {
|
|
36
|
+
const cmd = r.command.split(/\s+/).slice(0, 2).join(' ');
|
|
37
|
+
cmdCounts.set(cmd, (cmdCounts.get(cmd) ?? 0) + 1);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const topCmds = [...cmdCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 10);
|
|
41
|
+
if (topCmds.length > 0) {
|
|
42
|
+
console.log('\nTop commands:');
|
|
43
|
+
for (const [cmd, count] of topCmds) {
|
|
44
|
+
console.log(` ${count.toString().padStart(5)} ${cmd}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=run_stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run_stats.js","sourceRoot":"","sources":["../../../server/src/cli/run_stats.ts"],"names":[],"mappings":"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"}
|
package/dist/server/cli.js
CHANGED
|
@@ -1,253 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { createApprovalStore } from './domains/command-gateway/repository/approval_store.js';
|
|
10
|
-
import { runTelegramPairing } from './domains/command-gateway/service/telegram_pairing.js';
|
|
11
|
-
import { redactApiKeyName } from './domains/command-gateway/service/redact_api_key_name.js';
|
|
12
|
-
import { getTelegramToken } from './domains/command-gateway/config/gateway_config.js';
|
|
13
|
-
import { updateJsonConfig } from './lib/json_config_writer.js';
|
|
14
|
-
import { logger } from './lib/logger.js';
|
|
2
|
+
import { getArgValue } from './cli/args.js';
|
|
3
|
+
import { printHelp } from './cli/print_help.js';
|
|
4
|
+
import { initConfig } from './cli/init_config.js';
|
|
5
|
+
import { runLog } from './cli/run_log.js';
|
|
6
|
+
import { runStats } from './cli/run_stats.js';
|
|
7
|
+
import { runPair } from './cli/run_pair.js';
|
|
8
|
+
import { runServer } from './cli/run_server.js';
|
|
15
9
|
const args = process.argv.slice(2);
|
|
16
|
-
function printHelp() {
|
|
17
|
-
console.log(`
|
|
18
|
-
lucifer-gate - AI Agent Command Firewall
|
|
19
|
-
|
|
20
|
-
Usage:
|
|
21
|
-
lucifer-gate start [options] Start the server (explicit form)
|
|
22
|
-
lucifer-gate [options] Start the server (implicit, same as 'start')
|
|
23
|
-
lucifer-gate --init [dir] Generate starter config files
|
|
24
|
-
lucifer-gate pair [--config <path>] Pair a Telegram chat for approvals
|
|
25
|
-
lucifer-gate log [--limit N] Query audit log
|
|
26
|
-
lucifer-gate stats Show approval statistics
|
|
27
|
-
|
|
28
|
-
Server options:
|
|
29
|
-
--config <path> Path to lucifer.json (default: ./config/lucifer.json)
|
|
30
|
-
--port <number> Server port (default: 3001, or PORT env var)
|
|
31
|
-
--data-dir <path> Directory for SQLite database (default: ./data)
|
|
32
|
-
--auto-approve Auto-approve all commands (dev mode, no Telegram needed)
|
|
33
|
-
--help Show this help
|
|
34
|
-
|
|
35
|
-
Environment variables:
|
|
36
|
-
LUCIFER_TELEGRAM_TOKEN Telegram bot token (required for production)
|
|
37
|
-
LUCIFER_TELEGRAM_CHAT_ID Telegram chat ID for approvals (or use 'pair' command)
|
|
38
|
-
PORT Server port (default: 3001)
|
|
39
|
-
LOG_LEVEL Log level: debug, info, warn, error (default: debug / info in production)
|
|
40
|
-
`);
|
|
41
|
-
}
|
|
42
|
-
function initConfig(targetDir) {
|
|
43
|
-
const configDir = resolve(targetDir, 'config');
|
|
44
|
-
const dataDir = resolve(targetDir, 'data');
|
|
45
|
-
mkdirSync(configDir, { recursive: true });
|
|
46
|
-
mkdirSync(dataDir, { recursive: true });
|
|
47
|
-
const luciferJsonPath = join(configDir, 'lucifer.json');
|
|
48
|
-
const apiKeysPath = join(configDir, 'api-keys.json');
|
|
49
|
-
const commandRulesPath = join(configDir, 'command-rules.json');
|
|
50
|
-
const proxyConfigPath = join(configDir, 'proxy-config.json');
|
|
51
|
-
if (existsSync(luciferJsonPath)) {
|
|
52
|
-
console.log(`Config already exists: ${luciferJsonPath}`);
|
|
53
|
-
console.log('Delete it first if you want to regenerate.');
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
const { key, salt, keyHash } = generateApiKey();
|
|
57
|
-
const { secret: adminSecret, salt: adminSalt, secretHash: adminHash } = generateAdminSecret();
|
|
58
|
-
writeFileSync(luciferJsonPath, JSON.stringify({
|
|
59
|
-
port: 3001,
|
|
60
|
-
adminSecretHash: adminHash,
|
|
61
|
-
adminSecretSalt: adminSalt,
|
|
62
|
-
approvalTimeoutSeconds: 300,
|
|
63
|
-
executionTimeoutSeconds: 120,
|
|
64
|
-
maxConcurrentExecutions: 5,
|
|
65
|
-
maxOutputBytes: 10485760,
|
|
66
|
-
rateLimitPerMinute: 10,
|
|
67
|
-
onApprovalTimeout: 'deny',
|
|
68
|
-
dataDir: '../data',
|
|
69
|
-
logFile: 'lucifer.log',
|
|
70
|
-
}, null, 2) + '\n');
|
|
71
|
-
writeFileSync(apiKeysPath, JSON.stringify({
|
|
72
|
-
keys: [{
|
|
73
|
-
id: crypto.randomUUID(),
|
|
74
|
-
name: 'default',
|
|
75
|
-
keyHash,
|
|
76
|
-
salt,
|
|
77
|
-
allowedIps: [],
|
|
78
|
-
createdAt: new Date().toISOString(),
|
|
79
|
-
active: true,
|
|
80
|
-
}],
|
|
81
|
-
}, null, 2) + '\n');
|
|
82
|
-
writeFileSync(commandRulesPath, JSON.stringify({
|
|
83
|
-
rules: [
|
|
84
|
-
{ prefix: 'echo ', action: 'always_approve' },
|
|
85
|
-
{ prefix: 'git status', action: 'always_approve' },
|
|
86
|
-
{ prefix: 'git pull', action: 'manual_approve' },
|
|
87
|
-
{ prefix: 'git push', action: 'manual_approve' },
|
|
88
|
-
{ prefix: 'npm test', action: 'always_approve' },
|
|
89
|
-
{ prefix: 'npm run', action: 'manual_approve' },
|
|
90
|
-
{ prefix: 'rm ', action: 'always_deny' },
|
|
91
|
-
],
|
|
92
|
-
defaultAction: 'always_deny',
|
|
93
|
-
}, null, 2) + '\n');
|
|
94
|
-
writeFileSync(proxyConfigPath, JSON.stringify({
|
|
95
|
-
proxies: [],
|
|
96
|
-
}, null, 2) + '\n');
|
|
97
|
-
console.log('Config files generated:');
|
|
98
|
-
console.log(` ${luciferJsonPath}`);
|
|
99
|
-
console.log(` ${apiKeysPath}`);
|
|
100
|
-
console.log(` ${commandRulesPath}`);
|
|
101
|
-
console.log(` ${proxyConfigPath}`);
|
|
102
|
-
console.log('');
|
|
103
|
-
console.log('Your API key (save this, it cannot be recovered):');
|
|
104
|
-
console.log(` ${key}`);
|
|
105
|
-
console.log('');
|
|
106
|
-
console.log('Your admin secret for the web approval UI (save this, it cannot be recovered):');
|
|
107
|
-
console.log(` ${adminSecret}`);
|
|
108
|
-
console.log('');
|
|
109
|
-
console.log('Quick start:');
|
|
110
|
-
console.log(' # Dev mode (no Telegram needed):');
|
|
111
|
-
console.log(` LUCIFER_TELEGRAM_TOKEN=skip lucifer-gate --config ${luciferJsonPath} --auto-approve`);
|
|
112
|
-
console.log('');
|
|
113
|
-
console.log(' # Production — pair your Telegram chat first:');
|
|
114
|
-
console.log(` LUCIFER_TELEGRAM_TOKEN=your_token lucifer-gate pair --config ${luciferJsonPath}`);
|
|
115
|
-
console.log('');
|
|
116
|
-
console.log(' # Then start the server:');
|
|
117
|
-
console.log(` LUCIFER_TELEGRAM_TOKEN=your_token lucifer-gate --config ${luciferJsonPath}`);
|
|
118
|
-
}
|
|
119
|
-
async function runLog(limit) {
|
|
120
|
-
const dataDir = getArgValue('--data-dir') ?? './data';
|
|
121
|
-
const db = getDatabase(dataDir);
|
|
122
|
-
const auditLog = createAuditLog(db);
|
|
123
|
-
const entries = auditLog.query(limit);
|
|
124
|
-
if (entries.length === 0) {
|
|
125
|
-
console.log('No audit log entries found.');
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
const reversed = [...entries].reverse();
|
|
129
|
-
for (const entry of reversed) {
|
|
130
|
-
const time = entry.ts.replace('T', ' ').replace(/\.\d+Z$/, 'Z');
|
|
131
|
-
const parts = [time, entry.type.toUpperCase().padEnd(16)];
|
|
132
|
-
if (entry.command)
|
|
133
|
-
parts.push(entry.command);
|
|
134
|
-
if (entry.apiKeyName)
|
|
135
|
-
parts.push(`key=${redactApiKeyName(entry.apiKeyName)}`);
|
|
136
|
-
if (entry.exitCode !== undefined && entry.exitCode !== null)
|
|
137
|
-
parts.push(`exit=${entry.exitCode}`);
|
|
138
|
-
if (entry.durationMs !== undefined && entry.durationMs !== null)
|
|
139
|
-
parts.push(`${entry.durationMs}ms`);
|
|
140
|
-
if (entry.error)
|
|
141
|
-
parts.push(`error: ${entry.error}`);
|
|
142
|
-
console.log(parts.join(' '));
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
async function runStats() {
|
|
146
|
-
const dataDir = getArgValue('--data-dir') ?? './data';
|
|
147
|
-
const db = getDatabase(dataDir);
|
|
148
|
-
const auditLog = createAuditLog(db);
|
|
149
|
-
const approvalStore = createApprovalStore(db);
|
|
150
|
-
const allEntries = auditLog.query(10000);
|
|
151
|
-
const requests = allEntries.filter(e => e.type === 'request');
|
|
152
|
-
const approvals = allEntries.filter(e => e.type === 'approved');
|
|
153
|
-
const denials = allEntries.filter(e => e.type === 'denied');
|
|
154
|
-
const executions = allEntries.filter(e => e.type === 'executed');
|
|
155
|
-
const activeApprovals = approvalStore.listAll(10000);
|
|
156
|
-
const expired = activeApprovals.filter(a => a.expiresAt && new Date(a.expiresAt) < new Date());
|
|
157
|
-
console.log('Lucifer Stats');
|
|
158
|
-
console.log('=============');
|
|
159
|
-
console.log(`Total requests: ${requests.length}`);
|
|
160
|
-
console.log(`Approved: ${approvals.length}`);
|
|
161
|
-
console.log(`Denied: ${denials.length}`);
|
|
162
|
-
console.log(`Executed: ${executions.length}`);
|
|
163
|
-
console.log(`Active approvals: ${activeApprovals.length - expired.length}`);
|
|
164
|
-
console.log(`Expired approvals: ${expired.length}`);
|
|
165
|
-
if (executions.length > 0) {
|
|
166
|
-
const durations = executions.filter(e => e.durationMs != null).map(e => e.durationMs);
|
|
167
|
-
if (durations.length > 0) {
|
|
168
|
-
const avg = Math.round(durations.reduce((a, b) => a + b, 0) / durations.length);
|
|
169
|
-
const max = Math.max(...durations);
|
|
170
|
-
console.log(`Avg exec time: ${avg}ms`);
|
|
171
|
-
console.log(`Max exec time: ${max}ms`);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
// Top commands
|
|
175
|
-
const cmdCounts = new Map();
|
|
176
|
-
for (const r of requests) {
|
|
177
|
-
if (r.command) {
|
|
178
|
-
const cmd = r.command.split(/\s+/).slice(0, 2).join(' ');
|
|
179
|
-
cmdCounts.set(cmd, (cmdCounts.get(cmd) ?? 0) + 1);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
const topCmds = [...cmdCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 10);
|
|
183
|
-
if (topCmds.length > 0) {
|
|
184
|
-
console.log('\nTop commands:');
|
|
185
|
-
for (const [cmd, count] of topCmds) {
|
|
186
|
-
console.log(` ${count.toString().padStart(5)} ${cmd}`);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
function createReadlinePairingIO() {
|
|
191
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
192
|
-
return {
|
|
193
|
-
print(msg) {
|
|
194
|
-
console.log(msg);
|
|
195
|
-
},
|
|
196
|
-
async choose(prompt, options) {
|
|
197
|
-
console.log(`\n${prompt}`);
|
|
198
|
-
for (let i = 0; i < options.length; i++) {
|
|
199
|
-
console.log(` ${i + 1}. ${options[i]}`);
|
|
200
|
-
}
|
|
201
|
-
while (true) {
|
|
202
|
-
const answer = await rl.question(`\nEnter number (1-${options.length}): `);
|
|
203
|
-
const num = Number.parseInt(answer.trim(), 10);
|
|
204
|
-
if (num >= 1 && num <= options.length)
|
|
205
|
-
return num - 1;
|
|
206
|
-
console.log(`Please enter a number between 1 and ${options.length}.`);
|
|
207
|
-
}
|
|
208
|
-
},
|
|
209
|
-
async confirm(prompt) {
|
|
210
|
-
const answer = await rl.question(`${prompt} [y/N] `);
|
|
211
|
-
return /^y(es)?$/i.test(answer.trim());
|
|
212
|
-
},
|
|
213
|
-
async prompt(msg) {
|
|
214
|
-
return rl.question(`${msg} `);
|
|
215
|
-
},
|
|
216
|
-
close() {
|
|
217
|
-
rl.close();
|
|
218
|
-
},
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
async function runPair() {
|
|
222
|
-
const configPath = resolve(getArgValue('--config') ?? './config/lucifer.json');
|
|
223
|
-
let token;
|
|
224
|
-
try {
|
|
225
|
-
token = getTelegramToken();
|
|
226
|
-
}
|
|
227
|
-
catch {
|
|
228
|
-
console.error('LUCIFER_TELEGRAM_TOKEN environment variable is required for pairing.\n' +
|
|
229
|
-
'Create a bot via @BotFather on Telegram and set the token:\n\n' +
|
|
230
|
-
' LUCIFER_TELEGRAM_TOKEN=your_token lucifer-gate pair');
|
|
231
|
-
process.exit(1);
|
|
232
|
-
}
|
|
233
|
-
const io = createReadlinePairingIO();
|
|
234
|
-
try {
|
|
235
|
-
const result = await runTelegramPairing(token, io, { waitForChats: true });
|
|
236
|
-
updateJsonConfig(configPath, { telegramChatId: result.chatId });
|
|
237
|
-
console.log(`\nChat ID ${result.chatId} saved to ${configPath}`);
|
|
238
|
-
console.log('You can now start Lucifer without LUCIFER_TELEGRAM_CHAT_ID:');
|
|
239
|
-
console.log(`\n LUCIFER_TELEGRAM_TOKEN=your_token lucifer-gate start --config ${configPath}`);
|
|
240
|
-
}
|
|
241
|
-
finally {
|
|
242
|
-
io.close();
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
function getArgValue(flag) {
|
|
246
|
-
const idx = args.indexOf(flag);
|
|
247
|
-
if (idx === -1 || idx >= args.length - 1)
|
|
248
|
-
return undefined;
|
|
249
|
-
return args[idx + 1];
|
|
250
|
-
}
|
|
251
10
|
async function main() {
|
|
252
11
|
if (args.includes('--help') || args.includes('-h')) {
|
|
253
12
|
printHelp();
|
|
@@ -259,16 +18,16 @@ async function main() {
|
|
|
259
18
|
process.exit(0);
|
|
260
19
|
}
|
|
261
20
|
if (args[0] === 'pair') {
|
|
262
|
-
await runPair();
|
|
21
|
+
await runPair(getArgValue(args, '--config') ?? './config/lucifer.json');
|
|
263
22
|
process.exit(0);
|
|
264
23
|
}
|
|
265
24
|
if (args[0] === 'log') {
|
|
266
|
-
const limitStr = getArgValue('--limit');
|
|
267
|
-
await runLog(limitStr ? parseInt(limitStr, 10) : 50);
|
|
25
|
+
const limitStr = getArgValue(args, '--limit');
|
|
26
|
+
await runLog(limitStr ? parseInt(limitStr, 10) : 50, getArgValue(args, '--data-dir') ?? './data');
|
|
268
27
|
process.exit(0);
|
|
269
28
|
}
|
|
270
29
|
if (args[0] === 'stats') {
|
|
271
|
-
await runStats();
|
|
30
|
+
await runStats(getArgValue(args, '--data-dir') ?? './data');
|
|
272
31
|
process.exit(0);
|
|
273
32
|
}
|
|
274
33
|
// Server mode — either `start` (explicit) or no subcommand (implicit).
|
|
@@ -280,28 +39,11 @@ async function main() {
|
|
|
280
39
|
console.error(`Run 'lucifer-gate --help' for usage.`);
|
|
281
40
|
process.exit(1);
|
|
282
41
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
process.env.PORT = port;
|
|
288
|
-
}
|
|
289
|
-
const { app, config, start, stop } = createApp({
|
|
290
|
-
configPath,
|
|
291
|
-
autoApprove,
|
|
42
|
+
await runServer({
|
|
43
|
+
configPath: getArgValue(args, '--config') ?? './config/lucifer.json',
|
|
44
|
+
port: getArgValue(args, '--port'),
|
|
45
|
+
autoApprove: args.includes('--auto-approve'),
|
|
292
46
|
});
|
|
293
|
-
const server = app.listen(config.port, async () => {
|
|
294
|
-
logger.info({ port: config.port, autoApprove }, 'Lucifer listening');
|
|
295
|
-
await start();
|
|
296
|
-
});
|
|
297
|
-
const shutdown = async () => {
|
|
298
|
-
logger.info('Shutting down');
|
|
299
|
-
await stop();
|
|
300
|
-
server.close();
|
|
301
|
-
process.exit(0);
|
|
302
|
-
};
|
|
303
|
-
process.on('SIGTERM', shutdown);
|
|
304
|
-
process.on('SIGINT', shutdown);
|
|
305
47
|
}
|
|
306
48
|
try {
|
|
307
49
|
await main();
|