feat: add OpenAI OAuth, strict model overrides, and Gmail pull mode
This commit is contained in:
@@ -72,4 +72,19 @@ describe('HookEngine', () => {
|
||||
expect(result.approved).toBe(false);
|
||||
expect(result.reason).toBe('Too dangerous');
|
||||
});
|
||||
|
||||
it('uses interactive confirmer when set (no pending queue)', async () => {
|
||||
const engine = new HookEngine({ confirm: ['shell.*'], log: [], silent: [] });
|
||||
const confirmer = vi.fn(async () => ({ approved: true }));
|
||||
engine.setInteractiveConfirmer(confirmer);
|
||||
|
||||
const result = await engine.requestConfirmation('shell.exec', { cmd: 'ls' });
|
||||
expect(result.approved).toBe(true);
|
||||
expect(engine.getPendingConfirmations()).toHaveLength(0);
|
||||
expect(confirmer).toHaveBeenCalledOnce();
|
||||
expect(confirmer).toHaveBeenCalledWith(expect.objectContaining({
|
||||
tool: 'shell.exec',
|
||||
args: { cmd: 'ls' },
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,16 +1,32 @@
|
||||
import { randomUUID } from 'crypto';
|
||||
import type { HookAction, HookResult, PendingConfirmation, HookConfig } from './types.js';
|
||||
|
||||
export type InteractiveConfirmer = (pending: {
|
||||
id: string;
|
||||
tool: string;
|
||||
args: Record<string, unknown>;
|
||||
}) => Promise<HookResult>;
|
||||
|
||||
export class HookEngine {
|
||||
private confirmPatterns: RegExp[];
|
||||
private logPatterns: RegExp[];
|
||||
private pendingConfirmations: Map<string, PendingConfirmation> = new Map();
|
||||
private interactiveConfirmer?: InteractiveConfirmer;
|
||||
|
||||
constructor(config: HookConfig) {
|
||||
this.confirmPatterns = config.confirm.map(p => this.patternToRegex(p));
|
||||
this.logPatterns = config.log.map(p => this.patternToRegex(p));
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional interactive confirmation handler.
|
||||
* When set, confirmation requests are handled immediately (no pending queue).
|
||||
* Useful for CLI/TUI environments where we can prompt the user inline.
|
||||
*/
|
||||
setInteractiveConfirmer(confirmer: InteractiveConfirmer | undefined): void {
|
||||
this.interactiveConfirmer = confirmer;
|
||||
}
|
||||
|
||||
private patternToRegex(pattern: string): RegExp {
|
||||
const escaped = pattern
|
||||
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
||||
@@ -31,6 +47,10 @@ export class HookEngine {
|
||||
async requestConfirmation(tool: string, args: Record<string, unknown>): Promise<HookResult> {
|
||||
const id = randomUUID();
|
||||
|
||||
if (this.interactiveConfirmer) {
|
||||
return await this.interactiveConfirmer({ id, tool, args });
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const pending: PendingConfirmation = {
|
||||
id,
|
||||
|
||||
Reference in New Issue
Block a user