bashbros 0.1.1 → 0.1.3
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/{chunk-VVSCAH2B.js → chunk-2RPTM6EQ.js} +211 -8
- package/dist/chunk-2RPTM6EQ.js.map +1 -0
- package/dist/chunk-EYO44OMN.js +181 -0
- package/dist/chunk-EYO44OMN.js.map +1 -0
- package/dist/chunk-FRMAIRQ2.js +89 -0
- package/dist/chunk-FRMAIRQ2.js.map +1 -0
- package/dist/chunk-JYWQT2B4.js +866 -0
- package/dist/chunk-JYWQT2B4.js.map +1 -0
- package/dist/{chunk-GD5VNHIN.js → chunk-QWZGB4V3.js} +4 -85
- package/dist/chunk-QWZGB4V3.js.map +1 -0
- package/dist/cli.js +520 -23
- package/dist/cli.js.map +1 -1
- package/dist/{db-EHQDB5OL.js → db-SWJUUSFX.js} +2 -2
- package/dist/engine-EGPAS2EX.js +10 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/session-Y4MICATZ.js +15 -0
- package/dist/session-Y4MICATZ.js.map +1 -0
- package/dist/static/index.html +1873 -276
- package/dist/writer-4ZEAKUFD.js +12 -0
- package/dist/writer-4ZEAKUFD.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-CSRPOGHY.js +0 -354
- package/dist/chunk-CSRPOGHY.js.map +0 -1
- package/dist/chunk-GD5VNHIN.js.map +0 -1
- package/dist/chunk-VVSCAH2B.js.map +0 -1
- package/dist/engine-PKLXW6OF.js +0 -9
- /package/dist/{db-EHQDB5OL.js.map → db-SWJUUSFX.js.map} +0 -0
- /package/dist/{engine-PKLXW6OF.js.map → engine-EGPAS2EX.js.map} +0 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
DashboardDB
|
|
4
|
+
} from "./chunk-JYWQT2B4.js";
|
|
5
|
+
|
|
6
|
+
// src/dashboard/writer.ts
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
import { mkdirSync, existsSync } from "fs";
|
|
10
|
+
function getDefaultDbPath() {
|
|
11
|
+
const bashbrosDir = join(homedir(), ".bashbros");
|
|
12
|
+
if (!existsSync(bashbrosDir)) {
|
|
13
|
+
mkdirSync(bashbrosDir, { recursive: true });
|
|
14
|
+
}
|
|
15
|
+
return join(bashbrosDir, "dashboard.db");
|
|
16
|
+
}
|
|
17
|
+
var DashboardWriter = class {
|
|
18
|
+
db;
|
|
19
|
+
sessionId = null;
|
|
20
|
+
commandCount = 0;
|
|
21
|
+
blockedCount = 0;
|
|
22
|
+
totalRiskScore = 0;
|
|
23
|
+
constructor(dbPath) {
|
|
24
|
+
const path = dbPath ?? getDefaultDbPath();
|
|
25
|
+
this.db = new DashboardDB(path);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Start a new watch session
|
|
29
|
+
*/
|
|
30
|
+
startSession(agent, workingDir) {
|
|
31
|
+
this.sessionId = this.db.insertSession({
|
|
32
|
+
agent,
|
|
33
|
+
pid: process.pid,
|
|
34
|
+
workingDir
|
|
35
|
+
});
|
|
36
|
+
this.commandCount = 0;
|
|
37
|
+
this.blockedCount = 0;
|
|
38
|
+
this.totalRiskScore = 0;
|
|
39
|
+
return this.sessionId;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* End the current session
|
|
43
|
+
*/
|
|
44
|
+
endSession() {
|
|
45
|
+
if (!this.sessionId) return;
|
|
46
|
+
const avgRiskScore = this.commandCount > 0 ? this.totalRiskScore / this.commandCount : 0;
|
|
47
|
+
this.db.updateSession(this.sessionId, {
|
|
48
|
+
endTime: /* @__PURE__ */ new Date(),
|
|
49
|
+
status: "completed",
|
|
50
|
+
commandCount: this.commandCount,
|
|
51
|
+
blockedCount: this.blockedCount,
|
|
52
|
+
avgRiskScore
|
|
53
|
+
});
|
|
54
|
+
this.sessionId = null;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Mark session as crashed (for unexpected exits)
|
|
58
|
+
*/
|
|
59
|
+
crashSession() {
|
|
60
|
+
if (!this.sessionId) return;
|
|
61
|
+
const avgRiskScore = this.commandCount > 0 ? this.totalRiskScore / this.commandCount : 0;
|
|
62
|
+
this.db.updateSession(this.sessionId, {
|
|
63
|
+
endTime: /* @__PURE__ */ new Date(),
|
|
64
|
+
status: "crashed",
|
|
65
|
+
commandCount: this.commandCount,
|
|
66
|
+
blockedCount: this.blockedCount,
|
|
67
|
+
avgRiskScore
|
|
68
|
+
});
|
|
69
|
+
this.sessionId = null;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Record a command execution
|
|
73
|
+
*/
|
|
74
|
+
recordCommand(command, allowed, riskScore, violations, durationMs) {
|
|
75
|
+
if (!this.sessionId) return null;
|
|
76
|
+
const input = {
|
|
77
|
+
sessionId: this.sessionId,
|
|
78
|
+
command,
|
|
79
|
+
allowed,
|
|
80
|
+
riskScore: riskScore.score,
|
|
81
|
+
riskLevel: riskScore.level,
|
|
82
|
+
riskFactors: riskScore.factors,
|
|
83
|
+
durationMs,
|
|
84
|
+
violations: violations.map((v) => v.message)
|
|
85
|
+
};
|
|
86
|
+
const id = this.db.insertCommand(input);
|
|
87
|
+
this.commandCount++;
|
|
88
|
+
this.totalRiskScore += riskScore.score;
|
|
89
|
+
if (!allowed) {
|
|
90
|
+
this.blockedCount++;
|
|
91
|
+
}
|
|
92
|
+
if (this.commandCount % 10 === 0) {
|
|
93
|
+
const avgRiskScore = this.totalRiskScore / this.commandCount;
|
|
94
|
+
this.db.updateSession(this.sessionId, {
|
|
95
|
+
commandCount: this.commandCount,
|
|
96
|
+
blockedCount: this.blockedCount,
|
|
97
|
+
avgRiskScore
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return id;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Record a Bash Bro AI event
|
|
104
|
+
*/
|
|
105
|
+
recordBroEvent(input) {
|
|
106
|
+
const dbInput = {
|
|
107
|
+
sessionId: this.sessionId ?? void 0,
|
|
108
|
+
eventType: input.eventType,
|
|
109
|
+
inputContext: input.inputContext,
|
|
110
|
+
outputSummary: input.outputSummary,
|
|
111
|
+
modelUsed: input.modelUsed,
|
|
112
|
+
latencyMs: input.latencyMs,
|
|
113
|
+
success: input.success
|
|
114
|
+
};
|
|
115
|
+
return this.db.insertBroEvent(dbInput);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Update Bash Bro status
|
|
119
|
+
*/
|
|
120
|
+
updateBroStatus(status) {
|
|
121
|
+
const dbInput = {
|
|
122
|
+
ollamaAvailable: status.ollamaAvailable,
|
|
123
|
+
ollamaModel: status.ollamaModel,
|
|
124
|
+
platform: status.platform,
|
|
125
|
+
shell: status.shell,
|
|
126
|
+
projectType: status.projectType
|
|
127
|
+
};
|
|
128
|
+
return this.db.updateBroStatus(dbInput);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Record a generic tool use (for all Claude Code tools)
|
|
132
|
+
*/
|
|
133
|
+
recordToolUse(input) {
|
|
134
|
+
const dbInput = {
|
|
135
|
+
toolName: input.toolName,
|
|
136
|
+
toolInput: input.toolInput,
|
|
137
|
+
toolOutput: input.toolOutput,
|
|
138
|
+
exitCode: input.exitCode,
|
|
139
|
+
success: input.success,
|
|
140
|
+
cwd: input.cwd,
|
|
141
|
+
repoName: input.repoName,
|
|
142
|
+
repoPath: input.repoPath
|
|
143
|
+
};
|
|
144
|
+
return this.db.insertToolUse(dbInput);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get current session ID
|
|
148
|
+
*/
|
|
149
|
+
getSessionId() {
|
|
150
|
+
return this.sessionId;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get current session stats
|
|
154
|
+
*/
|
|
155
|
+
getSessionStats() {
|
|
156
|
+
return {
|
|
157
|
+
commandCount: this.commandCount,
|
|
158
|
+
blockedCount: this.blockedCount,
|
|
159
|
+
avgRiskScore: this.commandCount > 0 ? this.totalRiskScore / this.commandCount : 0
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Close database connection
|
|
164
|
+
*/
|
|
165
|
+
close() {
|
|
166
|
+
this.db.close();
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Get the underlying database instance (for advanced use)
|
|
170
|
+
*/
|
|
171
|
+
getDB() {
|
|
172
|
+
return this.db;
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
var writer_default = DashboardWriter;
|
|
176
|
+
|
|
177
|
+
export {
|
|
178
|
+
DashboardWriter,
|
|
179
|
+
writer_default
|
|
180
|
+
};
|
|
181
|
+
//# sourceMappingURL=chunk-EYO44OMN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/dashboard/writer.ts"],"sourcesContent":["/**\r\n * Dashboard Writer Module\r\n * Bridge for watch mode to write monitoring data to the dashboard database\r\n */\r\n\r\nimport { homedir } from 'os'\r\nimport { join } from 'path'\r\nimport { mkdirSync, existsSync } from 'fs'\r\nimport { DashboardDB, type InsertCommandInput, type InsertBroEventInput, type InsertBroStatusInput, type InsertToolUseInput } from './db.js'\r\nimport type { RiskScore } from '../policy/risk-scorer.js'\r\nimport type { PolicyViolation } from '../types.js'\r\n\r\n// ─────────────────────────────────────────────────────────────\r\n// Types\r\n// ─────────────────────────────────────────────────────────────\r\n\r\nexport interface BroEventInput {\r\n eventType: string\r\n inputContext: string\r\n outputSummary: string\r\n modelUsed: string\r\n latencyMs: number\r\n success: boolean\r\n}\r\n\r\nexport interface BroStatusInput {\r\n ollamaAvailable: boolean\r\n ollamaModel: string\r\n platform: string\r\n shell: string\r\n projectType?: string\r\n}\r\n\r\nexport interface ToolUseInput {\r\n toolName: string\r\n toolInput: string\r\n toolOutput: string\r\n exitCode?: number | null\r\n success?: boolean | null\r\n cwd: string\r\n repoName?: string | null\r\n repoPath?: string | null\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────\r\n// Default Database Path\r\n// ─────────────────────────────────────────────────────────────\r\n\r\nfunction getDefaultDbPath(): string {\r\n const bashbrosDir = join(homedir(), '.bashbros')\r\n\r\n // Ensure directory exists\r\n if (!existsSync(bashbrosDir)) {\r\n mkdirSync(bashbrosDir, { recursive: true })\r\n }\r\n\r\n return join(bashbrosDir, 'dashboard.db')\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────\r\n// Dashboard Writer Class\r\n// ─────────────────────────────────────────────────────────────\r\n\r\nexport class DashboardWriter {\r\n private db: DashboardDB\r\n private sessionId: string | null = null\r\n private commandCount: number = 0\r\n private blockedCount: number = 0\r\n private totalRiskScore: number = 0\r\n\r\n constructor(dbPath?: string) {\r\n const path = dbPath ?? getDefaultDbPath()\r\n this.db = new DashboardDB(path)\r\n }\r\n\r\n /**\r\n * Start a new watch session\r\n */\r\n startSession(agent: string, workingDir: string): string {\r\n this.sessionId = this.db.insertSession({\r\n agent,\r\n pid: process.pid,\r\n workingDir\r\n })\r\n\r\n this.commandCount = 0\r\n this.blockedCount = 0\r\n this.totalRiskScore = 0\r\n\r\n return this.sessionId\r\n }\r\n\r\n /**\r\n * End the current session\r\n */\r\n endSession(): void {\r\n if (!this.sessionId) return\r\n\r\n const avgRiskScore = this.commandCount > 0\r\n ? this.totalRiskScore / this.commandCount\r\n : 0\r\n\r\n this.db.updateSession(this.sessionId, {\r\n endTime: new Date(),\r\n status: 'completed',\r\n commandCount: this.commandCount,\r\n blockedCount: this.blockedCount,\r\n avgRiskScore\r\n })\r\n\r\n this.sessionId = null\r\n }\r\n\r\n /**\r\n * Mark session as crashed (for unexpected exits)\r\n */\r\n crashSession(): void {\r\n if (!this.sessionId) return\r\n\r\n const avgRiskScore = this.commandCount > 0\r\n ? this.totalRiskScore / this.commandCount\r\n : 0\r\n\r\n this.db.updateSession(this.sessionId, {\r\n endTime: new Date(),\r\n status: 'crashed',\r\n commandCount: this.commandCount,\r\n blockedCount: this.blockedCount,\r\n avgRiskScore\r\n })\r\n\r\n this.sessionId = null\r\n }\r\n\r\n /**\r\n * Record a command execution\r\n */\r\n recordCommand(\r\n command: string,\r\n allowed: boolean,\r\n riskScore: RiskScore,\r\n violations: PolicyViolation[],\r\n durationMs: number\r\n ): string | null {\r\n if (!this.sessionId) return null\r\n\r\n const input: InsertCommandInput = {\r\n sessionId: this.sessionId,\r\n command,\r\n allowed,\r\n riskScore: riskScore.score,\r\n riskLevel: riskScore.level,\r\n riskFactors: riskScore.factors,\r\n durationMs,\r\n violations: violations.map(v => v.message)\r\n }\r\n\r\n const id = this.db.insertCommand(input)\r\n\r\n // Update session stats\r\n this.commandCount++\r\n this.totalRiskScore += riskScore.score\r\n if (!allowed) {\r\n this.blockedCount++\r\n }\r\n\r\n // Update session in DB periodically (every 10 commands)\r\n if (this.commandCount % 10 === 0) {\r\n const avgRiskScore = this.totalRiskScore / this.commandCount\r\n this.db.updateSession(this.sessionId, {\r\n commandCount: this.commandCount,\r\n blockedCount: this.blockedCount,\r\n avgRiskScore\r\n })\r\n }\r\n\r\n return id\r\n }\r\n\r\n /**\r\n * Record a Bash Bro AI event\r\n */\r\n recordBroEvent(input: BroEventInput): string {\r\n const dbInput: InsertBroEventInput = {\r\n sessionId: this.sessionId ?? undefined,\r\n eventType: input.eventType,\r\n inputContext: input.inputContext,\r\n outputSummary: input.outputSummary,\r\n modelUsed: input.modelUsed,\r\n latencyMs: input.latencyMs,\r\n success: input.success\r\n }\r\n\r\n return this.db.insertBroEvent(dbInput)\r\n }\r\n\r\n /**\r\n * Update Bash Bro status\r\n */\r\n updateBroStatus(status: BroStatusInput): string {\r\n const dbInput: InsertBroStatusInput = {\r\n ollamaAvailable: status.ollamaAvailable,\r\n ollamaModel: status.ollamaModel,\r\n platform: status.platform,\r\n shell: status.shell,\r\n projectType: status.projectType\r\n }\r\n\r\n return this.db.updateBroStatus(dbInput)\r\n }\r\n\r\n /**\r\n * Record a generic tool use (for all Claude Code tools)\r\n */\r\n recordToolUse(input: ToolUseInput): string {\r\n const dbInput: InsertToolUseInput = {\r\n toolName: input.toolName,\r\n toolInput: input.toolInput,\r\n toolOutput: input.toolOutput,\r\n exitCode: input.exitCode,\r\n success: input.success,\r\n cwd: input.cwd,\r\n repoName: input.repoName,\r\n repoPath: input.repoPath\r\n }\r\n\r\n return this.db.insertToolUse(dbInput)\r\n }\r\n\r\n /**\r\n * Get current session ID\r\n */\r\n getSessionId(): string | null {\r\n return this.sessionId\r\n }\r\n\r\n /**\r\n * Get current session stats\r\n */\r\n getSessionStats(): {\r\n commandCount: number\r\n blockedCount: number\r\n avgRiskScore: number\r\n } {\r\n return {\r\n commandCount: this.commandCount,\r\n blockedCount: this.blockedCount,\r\n avgRiskScore: this.commandCount > 0 ? this.totalRiskScore / this.commandCount : 0\r\n }\r\n }\r\n\r\n /**\r\n * Close database connection\r\n */\r\n close(): void {\r\n this.db.close()\r\n }\r\n\r\n /**\r\n * Get the underlying database instance (for advanced use)\r\n */\r\n getDB(): DashboardDB {\r\n return this.db\r\n }\r\n}\r\n\r\nexport default DashboardWriter\r\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/session.ts
|
|
4
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
import { homedir } from "os";
|
|
7
|
+
var SESSION_FILE = join(homedir(), ".bashbros", "session-allow.json");
|
|
8
|
+
function ensureDir() {
|
|
9
|
+
const dir = join(homedir(), ".bashbros");
|
|
10
|
+
if (!existsSync(dir)) {
|
|
11
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function loadSession() {
|
|
15
|
+
try {
|
|
16
|
+
if (!existsSync(SESSION_FILE)) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const data = JSON.parse(readFileSync(SESSION_FILE, "utf-8"));
|
|
20
|
+
if (data.pid !== process.pid) {
|
|
21
|
+
const age = Date.now() - data.startTime;
|
|
22
|
+
if (age > 24 * 60 * 60 * 1e3) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return data;
|
|
27
|
+
} catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function saveSession(data) {
|
|
32
|
+
ensureDir();
|
|
33
|
+
writeFileSync(SESSION_FILE, JSON.stringify(data, null, 2), { mode: 384 });
|
|
34
|
+
}
|
|
35
|
+
function getOrCreateSession() {
|
|
36
|
+
const existing = loadSession();
|
|
37
|
+
if (existing) {
|
|
38
|
+
return existing;
|
|
39
|
+
}
|
|
40
|
+
const newSession = {
|
|
41
|
+
pid: process.pid,
|
|
42
|
+
startTime: Date.now(),
|
|
43
|
+
allowedCommands: []
|
|
44
|
+
};
|
|
45
|
+
saveSession(newSession);
|
|
46
|
+
return newSession;
|
|
47
|
+
}
|
|
48
|
+
function allowForSession(command) {
|
|
49
|
+
const session = getOrCreateSession();
|
|
50
|
+
if (!session.allowedCommands.includes(command)) {
|
|
51
|
+
session.allowedCommands.push(command);
|
|
52
|
+
saveSession(session);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function isAllowedForSession(command) {
|
|
56
|
+
const session = loadSession();
|
|
57
|
+
if (!session) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
if (session.allowedCommands.includes(command)) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
for (const allowed of session.allowedCommands) {
|
|
64
|
+
if (allowed.endsWith("*")) {
|
|
65
|
+
const prefix = allowed.slice(0, -1);
|
|
66
|
+
if (command.startsWith(prefix)) {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
function getSessionAllowlist() {
|
|
74
|
+
const session = loadSession();
|
|
75
|
+
return session?.allowedCommands || [];
|
|
76
|
+
}
|
|
77
|
+
function clearSessionAllowlist() {
|
|
78
|
+
const session = getOrCreateSession();
|
|
79
|
+
session.allowedCommands = [];
|
|
80
|
+
saveSession(session);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export {
|
|
84
|
+
allowForSession,
|
|
85
|
+
isAllowedForSession,
|
|
86
|
+
getSessionAllowlist,
|
|
87
|
+
clearSessionAllowlist
|
|
88
|
+
};
|
|
89
|
+
//# sourceMappingURL=chunk-FRMAIRQ2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/session.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'\r\nimport { join } from 'path'\r\nimport { homedir } from 'os'\r\n\r\n/**\r\n * Session-based allowlist for temporary command permissions.\r\n * Stored in a temp file that gets cleared on restart.\r\n */\r\n\r\nconst SESSION_FILE = join(homedir(), '.bashbros', 'session-allow.json')\r\n\r\ninterface SessionData {\r\n pid: number\r\n startTime: number\r\n allowedCommands: string[]\r\n}\r\n\r\nfunction ensureDir(): void {\r\n const dir = join(homedir(), '.bashbros')\r\n if (!existsSync(dir)) {\r\n mkdirSync(dir, { recursive: true, mode: 0o700 })\r\n }\r\n}\r\n\r\nfunction loadSession(): SessionData | null {\r\n try {\r\n if (!existsSync(SESSION_FILE)) {\r\n return null\r\n }\r\n\r\n const data = JSON.parse(readFileSync(SESSION_FILE, 'utf-8'))\r\n\r\n // Check if session is from current process\r\n if (data.pid !== process.pid) {\r\n // Different process - check if it's stale (older than 24 hours)\r\n const age = Date.now() - data.startTime\r\n if (age > 24 * 60 * 60 * 1000) {\r\n return null\r\n }\r\n }\r\n\r\n return data\r\n } catch {\r\n return null\r\n }\r\n}\r\n\r\nfunction saveSession(data: SessionData): void {\r\n ensureDir()\r\n writeFileSync(SESSION_FILE, JSON.stringify(data, null, 2), { mode: 0o600 })\r\n}\r\n\r\nfunction getOrCreateSession(): SessionData {\r\n const existing = loadSession()\r\n\r\n if (existing) {\r\n return existing\r\n }\r\n\r\n const newSession: SessionData = {\r\n pid: process.pid,\r\n startTime: Date.now(),\r\n allowedCommands: []\r\n }\r\n\r\n saveSession(newSession)\r\n return newSession\r\n}\r\n\r\n/**\r\n * Add a command to the session allowlist\r\n */\r\nexport function allowForSession(command: string): void {\r\n const session = getOrCreateSession()\r\n\r\n if (!session.allowedCommands.includes(command)) {\r\n session.allowedCommands.push(command)\r\n saveSession(session)\r\n }\r\n}\r\n\r\n/**\r\n * Check if a command is allowed for this session\r\n */\r\nexport function isAllowedForSession(command: string): boolean {\r\n const session = loadSession()\r\n\r\n if (!session) {\r\n return false\r\n }\r\n\r\n // Check exact match\r\n if (session.allowedCommands.includes(command)) {\r\n return true\r\n }\r\n\r\n // Check pattern match (command starts with allowed pattern)\r\n for (const allowed of session.allowedCommands) {\r\n if (allowed.endsWith('*')) {\r\n const prefix = allowed.slice(0, -1)\r\n if (command.startsWith(prefix)) {\r\n return true\r\n }\r\n }\r\n }\r\n\r\n return false\r\n}\r\n\r\n/**\r\n * Get all commands allowed for this session\r\n */\r\nexport function getSessionAllowlist(): string[] {\r\n const session = loadSession()\r\n return session?.allowedCommands || []\r\n}\r\n\r\n/**\r\n * Clear the session allowlist\r\n */\r\nexport function clearSessionAllowlist(): void {\r\n const session = getOrCreateSession()\r\n session.allowedCommands = []\r\n saveSession(session)\r\n}\r\n"],"mappings":";;;AAAA,SAAS,YAAY,cAAc,eAAe,iBAAiB;AACnE,SAAS,YAAY;AACrB,SAAS,eAAe;AAOxB,IAAM,eAAe,KAAK,QAAQ,GAAG,aAAa,oBAAoB;AAQtE,SAAS,YAAkB;AACzB,QAAM,MAAM,KAAK,QAAQ,GAAG,WAAW;AACvC,MAAI,CAAC,WAAW,GAAG,GAAG;AACpB,cAAU,KAAK,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAAA,EACjD;AACF;AAEA,SAAS,cAAkC;AACzC,MAAI;AACF,QAAI,CAAC,WAAW,YAAY,GAAG;AAC7B,aAAO;AAAA,IACT;AAEA,UAAM,OAAO,KAAK,MAAM,aAAa,cAAc,OAAO,CAAC;AAG3D,QAAI,KAAK,QAAQ,QAAQ,KAAK;AAE5B,YAAM,MAAM,KAAK,IAAI,IAAI,KAAK;AAC9B,UAAI,MAAM,KAAK,KAAK,KAAK,KAAM;AAC7B,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,YAAY,MAAyB;AAC5C,YAAU;AACV,gBAAc,cAAc,KAAK,UAAU,MAAM,MAAM,CAAC,GAAG,EAAE,MAAM,IAAM,CAAC;AAC5E;AAEA,SAAS,qBAAkC;AACzC,QAAM,WAAW,YAAY;AAE7B,MAAI,UAAU;AACZ,WAAO;AAAA,EACT;AAEA,QAAM,aAA0B;AAAA,IAC9B,KAAK,QAAQ;AAAA,IACb,WAAW,KAAK,IAAI;AAAA,IACpB,iBAAiB,CAAC;AAAA,EACpB;AAEA,cAAY,UAAU;AACtB,SAAO;AACT;AAKO,SAAS,gBAAgB,SAAuB;AACrD,QAAM,UAAU,mBAAmB;AAEnC,MAAI,CAAC,QAAQ,gBAAgB,SAAS,OAAO,GAAG;AAC9C,YAAQ,gBAAgB,KAAK,OAAO;AACpC,gBAAY,OAAO;AAAA,EACrB;AACF;AAKO,SAAS,oBAAoB,SAA0B;AAC5D,QAAM,UAAU,YAAY;AAE5B,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,EACT;AAGA,MAAI,QAAQ,gBAAgB,SAAS,OAAO,GAAG;AAC7C,WAAO;AAAA,EACT;AAGA,aAAW,WAAW,QAAQ,iBAAiB;AAC7C,QAAI,QAAQ,SAAS,GAAG,GAAG;AACzB,YAAM,SAAS,QAAQ,MAAM,GAAG,EAAE;AAClC,UAAI,QAAQ,WAAW,MAAM,GAAG;AAC9B,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,sBAAgC;AAC9C,QAAM,UAAU,YAAY;AAC5B,SAAO,SAAS,mBAAmB,CAAC;AACtC;AAKO,SAAS,wBAA8B;AAC5C,QAAM,UAAU,mBAAmB;AACnC,UAAQ,kBAAkB,CAAC;AAC3B,cAAY,OAAO;AACrB;","names":[]}
|