codepiper 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.
Files changed (149) hide show
  1. package/.env.example +28 -0
  2. package/CHANGELOG.md +10 -0
  3. package/LEGAL_NOTICE.md +39 -0
  4. package/LICENSE +21 -0
  5. package/README.md +524 -0
  6. package/package.json +90 -0
  7. package/packages/cli/package.json +13 -0
  8. package/packages/cli/src/commands/analytics.ts +157 -0
  9. package/packages/cli/src/commands/attach.ts +299 -0
  10. package/packages/cli/src/commands/audit.ts +50 -0
  11. package/packages/cli/src/commands/auth.ts +261 -0
  12. package/packages/cli/src/commands/daemon.ts +162 -0
  13. package/packages/cli/src/commands/doctor.ts +303 -0
  14. package/packages/cli/src/commands/env-set.ts +162 -0
  15. package/packages/cli/src/commands/hook-forward.ts +268 -0
  16. package/packages/cli/src/commands/keys.ts +77 -0
  17. package/packages/cli/src/commands/kill.ts +19 -0
  18. package/packages/cli/src/commands/logs.ts +419 -0
  19. package/packages/cli/src/commands/model.ts +172 -0
  20. package/packages/cli/src/commands/policy-set.ts +185 -0
  21. package/packages/cli/src/commands/policy.ts +227 -0
  22. package/packages/cli/src/commands/providers.ts +114 -0
  23. package/packages/cli/src/commands/resize.ts +34 -0
  24. package/packages/cli/src/commands/send.ts +184 -0
  25. package/packages/cli/src/commands/sessions.ts +202 -0
  26. package/packages/cli/src/commands/slash.ts +92 -0
  27. package/packages/cli/src/commands/start.ts +243 -0
  28. package/packages/cli/src/commands/stop.ts +19 -0
  29. package/packages/cli/src/commands/tail.ts +137 -0
  30. package/packages/cli/src/commands/workflow.ts +786 -0
  31. package/packages/cli/src/commands/workspace.ts +127 -0
  32. package/packages/cli/src/lib/api.ts +78 -0
  33. package/packages/cli/src/lib/args.ts +72 -0
  34. package/packages/cli/src/lib/format.ts +93 -0
  35. package/packages/cli/src/main.ts +563 -0
  36. package/packages/core/package.json +7 -0
  37. package/packages/core/src/config.ts +30 -0
  38. package/packages/core/src/errors.ts +38 -0
  39. package/packages/core/src/eventBus.ts +56 -0
  40. package/packages/core/src/eventBusAdapter.ts +143 -0
  41. package/packages/core/src/index.ts +10 -0
  42. package/packages/core/src/sqliteEventBus.ts +336 -0
  43. package/packages/core/src/types.ts +63 -0
  44. package/packages/daemon/package.json +11 -0
  45. package/packages/daemon/src/api/analyticsRoutes.ts +343 -0
  46. package/packages/daemon/src/api/authRoutes.ts +344 -0
  47. package/packages/daemon/src/api/bodyLimit.ts +133 -0
  48. package/packages/daemon/src/api/envSetRoutes.ts +170 -0
  49. package/packages/daemon/src/api/gitRoutes.ts +409 -0
  50. package/packages/daemon/src/api/hooks.ts +588 -0
  51. package/packages/daemon/src/api/inputPolicy.ts +249 -0
  52. package/packages/daemon/src/api/notificationRoutes.ts +532 -0
  53. package/packages/daemon/src/api/policyRoutes.ts +234 -0
  54. package/packages/daemon/src/api/policySetRoutes.ts +445 -0
  55. package/packages/daemon/src/api/routeUtils.ts +28 -0
  56. package/packages/daemon/src/api/routes.ts +1004 -0
  57. package/packages/daemon/src/api/server.ts +1388 -0
  58. package/packages/daemon/src/api/settingsRoutes.ts +367 -0
  59. package/packages/daemon/src/api/sqliteErrors.ts +47 -0
  60. package/packages/daemon/src/api/stt.ts +143 -0
  61. package/packages/daemon/src/api/terminalRoutes.ts +200 -0
  62. package/packages/daemon/src/api/validation.ts +287 -0
  63. package/packages/daemon/src/api/validationRoutes.ts +174 -0
  64. package/packages/daemon/src/api/workflowRoutes.ts +567 -0
  65. package/packages/daemon/src/api/workspaceRoutes.ts +151 -0
  66. package/packages/daemon/src/api/ws.ts +1588 -0
  67. package/packages/daemon/src/auth/apiRateLimiter.ts +73 -0
  68. package/packages/daemon/src/auth/authMiddleware.ts +305 -0
  69. package/packages/daemon/src/auth/authService.ts +496 -0
  70. package/packages/daemon/src/auth/rateLimiter.ts +137 -0
  71. package/packages/daemon/src/config/pricing.ts +79 -0
  72. package/packages/daemon/src/crypto/encryption.ts +196 -0
  73. package/packages/daemon/src/db/db.ts +2745 -0
  74. package/packages/daemon/src/db/index.ts +16 -0
  75. package/packages/daemon/src/db/migrations.ts +182 -0
  76. package/packages/daemon/src/db/policyDb.ts +349 -0
  77. package/packages/daemon/src/db/schema.sql +408 -0
  78. package/packages/daemon/src/db/workflowDb.ts +464 -0
  79. package/packages/daemon/src/git/gitUtils.ts +544 -0
  80. package/packages/daemon/src/index.ts +6 -0
  81. package/packages/daemon/src/main.ts +525 -0
  82. package/packages/daemon/src/notifications/pushNotifier.ts +369 -0
  83. package/packages/daemon/src/providers/codexAppServerScaffold.ts +49 -0
  84. package/packages/daemon/src/providers/registry.ts +111 -0
  85. package/packages/daemon/src/providers/types.ts +82 -0
  86. package/packages/daemon/src/sessions/auditLogger.ts +103 -0
  87. package/packages/daemon/src/sessions/policyEngine.ts +165 -0
  88. package/packages/daemon/src/sessions/policyMatcher.ts +114 -0
  89. package/packages/daemon/src/sessions/policyTypes.ts +94 -0
  90. package/packages/daemon/src/sessions/ptyProcess.ts +141 -0
  91. package/packages/daemon/src/sessions/sessionManager.ts +1770 -0
  92. package/packages/daemon/src/sessions/tmuxSession.ts +1073 -0
  93. package/packages/daemon/src/sessions/transcriptManager.ts +110 -0
  94. package/packages/daemon/src/sessions/transcriptParser.ts +149 -0
  95. package/packages/daemon/src/sessions/transcriptTailer.ts +214 -0
  96. package/packages/daemon/src/tracking/tokenTracker.ts +168 -0
  97. package/packages/daemon/src/workflows/contextManager.ts +83 -0
  98. package/packages/daemon/src/workflows/index.ts +31 -0
  99. package/packages/daemon/src/workflows/resultExtractor.ts +118 -0
  100. package/packages/daemon/src/workflows/waitConditionPoller.ts +131 -0
  101. package/packages/daemon/src/workflows/workflowParser.ts +217 -0
  102. package/packages/daemon/src/workflows/workflowRunner.ts +969 -0
  103. package/packages/daemon/src/workflows/workflowTypes.ts +188 -0
  104. package/packages/daemon/src/workflows/workflowValidator.ts +533 -0
  105. package/packages/providers/claude-code/package.json +11 -0
  106. package/packages/providers/claude-code/src/index.ts +7 -0
  107. package/packages/providers/claude-code/src/overlaySettings.ts +198 -0
  108. package/packages/providers/claude-code/src/provider.ts +311 -0
  109. package/packages/web/dist/android-chrome-192x192.png +0 -0
  110. package/packages/web/dist/android-chrome-512x512.png +0 -0
  111. package/packages/web/dist/apple-touch-icon.png +0 -0
  112. package/packages/web/dist/assets/AnalyticsPage-BIopKWRf.js +17 -0
  113. package/packages/web/dist/assets/PoliciesPage-CjdLN3dl.js +11 -0
  114. package/packages/web/dist/assets/SessionDetailPage-BtSA0V0M.js +179 -0
  115. package/packages/web/dist/assets/SettingsPage-Dbbz4Ca5.js +37 -0
  116. package/packages/web/dist/assets/WorkflowsPage-Dv6f3GgU.js +1 -0
  117. package/packages/web/dist/assets/chart-vendor-DlOHLaCG.js +49 -0
  118. package/packages/web/dist/assets/codicon-ngg6Pgfi.ttf +0 -0
  119. package/packages/web/dist/assets/css.worker-BvV5MPou.js +93 -0
  120. package/packages/web/dist/assets/editor.worker-CKy7Pnvo.js +26 -0
  121. package/packages/web/dist/assets/html.worker-BLJhxQJQ.js +470 -0
  122. package/packages/web/dist/assets/index-BbdhRfr2.css +1 -0
  123. package/packages/web/dist/assets/index-hgphORiw.js +204 -0
  124. package/packages/web/dist/assets/json.worker-usMZ-FED.js +58 -0
  125. package/packages/web/dist/assets/monaco-core-B_19GPAS.css +1 -0
  126. package/packages/web/dist/assets/monaco-core-DQ5Mk8AK.js +1234 -0
  127. package/packages/web/dist/assets/monaco-react-DfZNWvtW.js +11 -0
  128. package/packages/web/dist/assets/monacoSetup-DvBj52bT.js +1 -0
  129. package/packages/web/dist/assets/pencil-Dbczxz59.js +11 -0
  130. package/packages/web/dist/assets/react-vendor-B5MgMUHH.js +136 -0
  131. package/packages/web/dist/assets/refresh-cw-B0MGsYPL.js +6 -0
  132. package/packages/web/dist/assets/tabs-C8LsWiR5.js +1 -0
  133. package/packages/web/dist/assets/terminal-vendor-Cs8KPbV3.js +9 -0
  134. package/packages/web/dist/assets/terminal-vendor-LcAfv9l9.css +32 -0
  135. package/packages/web/dist/assets/trash-2-Btlg0d4l.js +6 -0
  136. package/packages/web/dist/assets/ts.worker-DGHjMaqB.js +67731 -0
  137. package/packages/web/dist/favicon.ico +0 -0
  138. package/packages/web/dist/icon.svg +1 -0
  139. package/packages/web/dist/index.html +29 -0
  140. package/packages/web/dist/manifest.json +29 -0
  141. package/packages/web/dist/og-image.png +0 -0
  142. package/packages/web/dist/originals/android-chrome-192x192.png +0 -0
  143. package/packages/web/dist/originals/android-chrome-512x512.png +0 -0
  144. package/packages/web/dist/originals/apple-touch-icon.png +0 -0
  145. package/packages/web/dist/originals/favicon.ico +0 -0
  146. package/packages/web/dist/piper.svg +1 -0
  147. package/packages/web/dist/sounds/codepiper-soft-chime.wav +0 -0
  148. package/packages/web/dist/sw.js +257 -0
  149. package/scripts/postinstall-link-workspaces.mjs +58 -0
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Database module exports
3
+ */
4
+
5
+ export type {
6
+ CreateSessionParams,
7
+ EventRecord,
8
+ EventSource,
9
+ GetEventsOptions,
10
+ InsertEventParams,
11
+ ListSessionsOptions,
12
+ TranscriptOffset,
13
+ UpdateSessionParams,
14
+ } from "./db";
15
+ export { Database, type IDatabase } from "./db";
16
+ export { type Migration, MigrationManager } from "./migrations";
@@ -0,0 +1,182 @@
1
+ import type { Database as BunDatabase } from "bun:sqlite";
2
+
3
+ /**
4
+ * Migration definition
5
+ */
6
+ export interface Migration {
7
+ version: number;
8
+ name: string;
9
+ up: (db: BunDatabase) => void;
10
+ down?: (db: BunDatabase) => void;
11
+ }
12
+
13
+ /**
14
+ * Migration manager for schema versioning
15
+ */
16
+ export class MigrationManager {
17
+ private db: BunDatabase;
18
+ private migrations: Migration[];
19
+
20
+ constructor(db: BunDatabase) {
21
+ this.db = db;
22
+ this.migrations = [];
23
+ this.initMigrationsTable();
24
+ }
25
+
26
+ /**
27
+ * Create migrations tracking table if it doesn't exist
28
+ */
29
+ private initMigrationsTable(): void {
30
+ this.db.run(`
31
+ CREATE TABLE IF NOT EXISTS schema_migrations (
32
+ version INTEGER PRIMARY KEY,
33
+ name TEXT NOT NULL,
34
+ applied_at INTEGER NOT NULL
35
+ )
36
+ `);
37
+ }
38
+
39
+ /**
40
+ * Register a migration
41
+ */
42
+ register(migration: Migration): void {
43
+ this.migrations.push(migration);
44
+ // Keep migrations sorted by version
45
+ this.migrations.sort((a, b) => a.version - b.version);
46
+ }
47
+
48
+ /**
49
+ * Get current schema version
50
+ */
51
+ getCurrentVersion(): number {
52
+ const stmt = this.db.prepare("SELECT MAX(version) as version FROM schema_migrations");
53
+ const result = stmt.get() as { version: number | null };
54
+ return result.version ?? 0;
55
+ }
56
+
57
+ /**
58
+ * Get list of applied migrations
59
+ */
60
+ getAppliedMigrations(): number[] {
61
+ const stmt = this.db.prepare("SELECT version FROM schema_migrations ORDER BY version");
62
+ const rows = stmt.all() as { version: number }[];
63
+ return rows.map((row) => row.version);
64
+ }
65
+
66
+ /**
67
+ * Apply pending migrations
68
+ */
69
+ migrate(): void {
70
+ const currentVersion = this.getCurrentVersion();
71
+ const pendingMigrations = this.migrations.filter((m) => m.version > currentVersion);
72
+
73
+ if (pendingMigrations.length === 0) {
74
+ return;
75
+ }
76
+
77
+ for (const migration of pendingMigrations) {
78
+ console.log(`Applying migration ${migration.version}: ${migration.name}`);
79
+
80
+ try {
81
+ migration.up(this.db);
82
+
83
+ // Record migration
84
+ const stmt = this.db.prepare(
85
+ "INSERT INTO schema_migrations (version, name, applied_at) VALUES (?, ?, ?)"
86
+ );
87
+ stmt.run(migration.version, migration.name, Date.now());
88
+
89
+ console.log(`Migration ${migration.version} applied successfully`);
90
+ } catch (error) {
91
+ console.error(`Migration ${migration.version} failed:`, error);
92
+ throw error;
93
+ }
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Rollback migrations to a specific version
99
+ */
100
+ rollback(targetVersion: number): void {
101
+ const currentVersion = this.getCurrentVersion();
102
+
103
+ if (targetVersion >= currentVersion) {
104
+ return;
105
+ }
106
+
107
+ const migrationsToRollback = this.migrations
108
+ .filter((m) => m.version > targetVersion && m.version <= currentVersion)
109
+ .reverse(); // Rollback in reverse order
110
+
111
+ for (const migration of migrationsToRollback) {
112
+ if (!migration.down) {
113
+ throw new Error(
114
+ `Migration ${migration.version} (${migration.name}) does not support rollback`
115
+ );
116
+ }
117
+
118
+ console.log(`Rolling back migration ${migration.version}: ${migration.name}`);
119
+
120
+ try {
121
+ migration.down(this.db);
122
+
123
+ // Remove migration record
124
+ const stmt = this.db.prepare("DELETE FROM schema_migrations WHERE version = ?");
125
+ stmt.run(migration.version);
126
+
127
+ console.log(`Migration ${migration.version} rolled back successfully`);
128
+ } catch (error) {
129
+ console.error(`Rollback of migration ${migration.version} failed:`, error);
130
+ throw error;
131
+ }
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Reset database to initial state (rollback all migrations)
137
+ */
138
+ reset(): void {
139
+ this.rollback(0);
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Example migration definitions
145
+ *
146
+ * Note: The initial schema is loaded via schema.sql, so migrations
147
+ * are primarily for future schema changes after initial deployment.
148
+ */
149
+
150
+ /**
151
+ * Example: Add index for session timestamps
152
+ */
153
+ export const migration001: Migration = {
154
+ version: 1,
155
+ name: "add_session_timestamp_index",
156
+ up: (db) => {
157
+ db.run(`
158
+ CREATE INDEX IF NOT EXISTS idx_sessions_created_at
159
+ ON sessions(created_at DESC)
160
+ `);
161
+ },
162
+ down: (db) => {
163
+ db.run("DROP INDEX IF EXISTS idx_sessions_created_at");
164
+ },
165
+ };
166
+
167
+ /**
168
+ * Example: Add index for event timestamps
169
+ */
170
+ export const migration002: Migration = {
171
+ version: 2,
172
+ name: "add_event_timestamp_index",
173
+ up: (db) => {
174
+ db.run(`
175
+ CREATE INDEX IF NOT EXISTS idx_events_ts
176
+ ON events(ts DESC)
177
+ `);
178
+ },
179
+ down: (db) => {
180
+ db.run("DROP INDEX IF EXISTS idx_events_ts");
181
+ },
182
+ };
@@ -0,0 +1,349 @@
1
+ /**
2
+ * Policy database operations
3
+ */
4
+
5
+ import type { Database } from "./db";
6
+
7
+ /**
8
+ * Policy action types
9
+ */
10
+ export type PolicyAction = "allow" | "deny" | "ask";
11
+
12
+ /**
13
+ * Policy rule definition
14
+ */
15
+ export interface PolicyRule {
16
+ id: string;
17
+ action: PolicyAction;
18
+ tool?: string | string[];
19
+ args?: Record<string, string | string[]>;
20
+ cwd?: string | string[];
21
+ session?: string | string[];
22
+ reason?: string;
23
+ }
24
+
25
+ /**
26
+ * Policy record
27
+ */
28
+ export interface Policy {
29
+ id: string;
30
+ name: string;
31
+ description?: string;
32
+ enabled: boolean;
33
+ priority: number;
34
+ sessionId: string | null;
35
+ rules: PolicyRule[];
36
+ createdAt: Date;
37
+ updatedAt: Date;
38
+ }
39
+
40
+ /**
41
+ * Policy decision record
42
+ */
43
+ export interface PolicyDecision {
44
+ id: number;
45
+ sessionId: string;
46
+ eventId?: number;
47
+ policyId?: string;
48
+ toolName: string;
49
+ args?: Record<string, unknown>;
50
+ decision: PolicyAction;
51
+ reason?: string;
52
+ timestamp: Date;
53
+ }
54
+
55
+ /**
56
+ * Policy creation parameters
57
+ */
58
+ export interface CreatePolicyParams {
59
+ id: string;
60
+ name: string;
61
+ description?: string;
62
+ enabled: boolean;
63
+ priority: number;
64
+ sessionId: string | null;
65
+ rules: PolicyRule[];
66
+ }
67
+
68
+ /**
69
+ * Policy update parameters
70
+ */
71
+ export interface UpdatePolicyParams {
72
+ name?: string;
73
+ description?: string;
74
+ enabled?: boolean;
75
+ priority?: number;
76
+ rules?: PolicyRule[];
77
+ }
78
+
79
+ /**
80
+ * Policy query options
81
+ */
82
+ export interface GetPoliciesOptions {
83
+ enabled?: boolean;
84
+ sessionId?: string | null;
85
+ }
86
+
87
+ /**
88
+ * Policy decision insertion parameters
89
+ */
90
+ export interface InsertPolicyDecisionParams {
91
+ sessionId: string;
92
+ eventId?: number;
93
+ policyId?: string;
94
+ toolName: string;
95
+ args?: Record<string, unknown>;
96
+ decision: PolicyAction;
97
+ reason?: string;
98
+ timestamp?: Date;
99
+ }
100
+
101
+ /**
102
+ * Policy decision query options
103
+ */
104
+ export interface GetPolicyDecisionsOptions {
105
+ since?: Date;
106
+ limit?: number;
107
+ decision?: PolicyAction;
108
+ toolName?: string;
109
+ }
110
+
111
+ /**
112
+ * Policy database interface
113
+ */
114
+ export interface PolicyDb {
115
+ // Policy operations
116
+ createPolicy(params: CreatePolicyParams): void;
117
+ getPolicy(id: string): Policy | undefined;
118
+ updatePolicy(id: string, params: UpdatePolicyParams): void;
119
+ deletePolicy(id: string): void;
120
+ getPolicies(options?: GetPoliciesOptions): Policy[];
121
+
122
+ // Policy decision operations
123
+ insertPolicyDecision(params: InsertPolicyDecisionParams): number;
124
+ getPolicyDecisions(sessionId: string, options?: GetPolicyDecisionsOptions): PolicyDecision[];
125
+ }
126
+
127
+ /**
128
+ * Create policy database operations
129
+ */
130
+ export function createPolicyDb(db: Database): PolicyDb {
131
+ // Get access to underlying Bun database for raw queries
132
+ const bunDb = (db as any).db;
133
+
134
+ return {
135
+ /**
136
+ * Create a new policy
137
+ */
138
+ createPolicy(params: CreatePolicyParams): void {
139
+ const now = Date.now();
140
+ const stmt = bunDb.prepare(`
141
+ INSERT INTO policies (
142
+ id, name, description, enabled, priority, session_id,
143
+ rules_json, created_at, updated_at
144
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
145
+ `);
146
+
147
+ stmt.run(
148
+ params.id,
149
+ params.name,
150
+ params.description ?? null,
151
+ params.enabled ? 1 : 0,
152
+ params.priority,
153
+ params.sessionId,
154
+ JSON.stringify(params.rules),
155
+ now,
156
+ now
157
+ );
158
+ },
159
+
160
+ /**
161
+ * Get policy by ID
162
+ */
163
+ getPolicy(id: string): Policy | undefined {
164
+ const stmt = bunDb.prepare("SELECT * FROM policies WHERE id = ?");
165
+ const row = stmt.get(id) as any;
166
+
167
+ if (!row) return undefined;
168
+
169
+ return mapRowToPolicy(row);
170
+ },
171
+
172
+ /**
173
+ * Update policy fields
174
+ */
175
+ updatePolicy(id: string, params: UpdatePolicyParams): void {
176
+ const updates: string[] = [];
177
+ const values: unknown[] = [];
178
+
179
+ if (params.name !== undefined) {
180
+ updates.push("name = ?");
181
+ values.push(params.name);
182
+ }
183
+ if (params.description !== undefined) {
184
+ updates.push("description = ?");
185
+ values.push(params.description);
186
+ }
187
+ if (params.enabled !== undefined) {
188
+ updates.push("enabled = ?");
189
+ values.push(params.enabled ? 1 : 0);
190
+ }
191
+ if (params.priority !== undefined) {
192
+ updates.push("priority = ?");
193
+ values.push(params.priority);
194
+ }
195
+ if (params.rules !== undefined) {
196
+ updates.push("rules_json = ?");
197
+ values.push(JSON.stringify(params.rules));
198
+ }
199
+
200
+ // Always update updated_at
201
+ updates.push("updated_at = ?");
202
+ values.push(Date.now());
203
+
204
+ values.push(id);
205
+
206
+ const sql = `UPDATE policies SET ${updates.join(", ")} WHERE id = ?`;
207
+ const stmt = bunDb.prepare(sql);
208
+ stmt.run(...values);
209
+ },
210
+
211
+ /**
212
+ * Delete policy
213
+ */
214
+ deletePolicy(id: string): void {
215
+ const stmt = bunDb.prepare("DELETE FROM policies WHERE id = ?");
216
+ stmt.run(id);
217
+ },
218
+
219
+ /**
220
+ * Get policies with optional filters
221
+ */
222
+ getPolicies(options: GetPoliciesOptions = {}): Policy[] {
223
+ let sql = "SELECT * FROM policies WHERE 1=1";
224
+ const values: unknown[] = [];
225
+
226
+ if (options.enabled !== undefined) {
227
+ sql += " AND enabled = ?";
228
+ values.push(options.enabled ? 1 : 0);
229
+ }
230
+
231
+ if (options.sessionId !== undefined) {
232
+ if (options.sessionId === null) {
233
+ sql += " AND session_id IS NULL";
234
+ } else {
235
+ sql += " AND session_id = ?";
236
+ values.push(options.sessionId);
237
+ }
238
+ }
239
+
240
+ // Order by priority descending (higher priority first)
241
+ sql += " ORDER BY priority DESC";
242
+
243
+ const stmt = bunDb.prepare(sql);
244
+ const rows = stmt.all(...values) as any[];
245
+
246
+ return rows.map((row) => mapRowToPolicy(row));
247
+ },
248
+
249
+ /**
250
+ * Insert a policy decision
251
+ */
252
+ insertPolicyDecision(params: InsertPolicyDecisionParams): number {
253
+ const timestamp = params.timestamp ?? new Date();
254
+ const stmt = bunDb.prepare(`
255
+ INSERT INTO policy_decisions (
256
+ session_id, event_id, policy_id, tool_name, args_json,
257
+ decision, reason, timestamp
258
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
259
+ `);
260
+
261
+ stmt.run(
262
+ params.sessionId,
263
+ params.eventId ?? null,
264
+ params.policyId ?? null,
265
+ params.toolName,
266
+ params.args ? JSON.stringify(params.args) : null,
267
+ params.decision,
268
+ params.reason ?? null,
269
+ timestamp.getTime()
270
+ );
271
+
272
+ // Get last inserted row id
273
+ const result = bunDb.query("SELECT last_insert_rowid() as id").get() as { id: number };
274
+ return result.id;
275
+ },
276
+
277
+ /**
278
+ * Get policy decisions for a session with optional filters
279
+ */
280
+ getPolicyDecisions(
281
+ sessionId: string,
282
+ options: GetPolicyDecisionsOptions = {}
283
+ ): PolicyDecision[] {
284
+ let sql = "SELECT * FROM policy_decisions WHERE session_id = ?";
285
+ const values: unknown[] = [sessionId];
286
+
287
+ if (options.since !== undefined) {
288
+ sql += " AND timestamp > ?";
289
+ values.push(options.since.getTime());
290
+ }
291
+
292
+ if (options.decision) {
293
+ sql += " AND decision = ?";
294
+ values.push(options.decision);
295
+ }
296
+
297
+ if (options.toolName) {
298
+ sql += " AND tool_name = ?";
299
+ values.push(options.toolName);
300
+ }
301
+
302
+ sql += " ORDER BY id ASC";
303
+
304
+ if (options.limit) {
305
+ sql += " LIMIT ?";
306
+ values.push(options.limit);
307
+ }
308
+
309
+ const stmt = bunDb.prepare(sql);
310
+ const rows = stmt.all(...values) as any[];
311
+
312
+ return rows.map((row) => mapRowToPolicyDecision(row));
313
+ },
314
+ };
315
+ }
316
+
317
+ /**
318
+ * Map database row to Policy
319
+ */
320
+ function mapRowToPolicy(row: any): Policy {
321
+ return {
322
+ id: row.id,
323
+ name: row.name,
324
+ description: row.description ?? undefined,
325
+ enabled: row.enabled === 1,
326
+ priority: row.priority,
327
+ sessionId: row.session_id,
328
+ rules: JSON.parse(row.rules_json),
329
+ createdAt: new Date(row.created_at),
330
+ updatedAt: new Date(row.updated_at),
331
+ };
332
+ }
333
+
334
+ /**
335
+ * Map database row to PolicyDecision
336
+ */
337
+ function mapRowToPolicyDecision(row: any): PolicyDecision {
338
+ return {
339
+ id: row.id,
340
+ sessionId: row.session_id,
341
+ eventId: row.event_id ?? undefined,
342
+ policyId: row.policy_id ?? undefined,
343
+ toolName: row.tool_name,
344
+ args: row.args_json ? JSON.parse(row.args_json) : undefined,
345
+ decision: row.decision as PolicyAction,
346
+ reason: row.reason ?? undefined,
347
+ timestamp: new Date(row.timestamp),
348
+ };
349
+ }