feat(watcher): add agent Gmail CLI with audit and defaults

Provide list/read/summarize plus reason-gated archive, label, draft, and forward-as-draft, with SQLite audit logging and default-mailbox resolution.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-22 10:09:25 +07:00
co-authored by Cursor
parent 770ed8121a
commit f270f531ea
13 changed files with 1643 additions and 145 deletions
+32 -1
View File
@@ -28,6 +28,15 @@ db.exec(`
);
`);
function ensureColumn(table, column, definition) {
const cols = db.prepare(`PRAGMA table_info(${table})`).all();
if (!cols.some((c) => c.name === column)) {
db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
}
}
ensureColumn("users", "is_default", "INTEGER NOT NULL DEFAULT 0");
const upsertUserStmt = db.prepare(`
INSERT INTO users (google_sub, email, name, picture)
VALUES (@googleSub, @email, @name, @picture)
@@ -49,9 +58,21 @@ const saveTokensStmt = db.prepare(`
const getUserByIdStmt = db.prepare(`SELECT * FROM users WHERE id = ?`);
const getTokensStmt = db.prepare(`SELECT * FROM oauth_tokens WHERE user_id = ?`);
const countDefaultsStmt = db.prepare(
`SELECT COUNT(*) AS n FROM users WHERE is_default = 1`,
);
const clearDefaultsStmt = db.prepare(`UPDATE users SET is_default = 0`);
const setDefaultStmt = db.prepare(
`UPDATE users SET is_default = 1 WHERE id = ?`,
);
export function upsertUser({ googleSub, email, name, picture }) {
return upsertUserStmt.get({ googleSub, email, name, picture });
const user = upsertUserStmt.get({ googleSub, email, name, picture });
if (user && countDefaultsStmt.get().n === 0) {
setDefaultStmt.run(user.id);
return getUserByIdStmt.get(user.id);
}
return user;
}
export function saveTokens(userId, { accessToken, refreshToken, expiry }) {
@@ -71,6 +92,15 @@ export function getTokens(userId) {
return getTokensStmt.get(userId) || null;
}
export function setDefaultUser(userId) {
const clearAndSet = db.transaction((id) => {
clearDefaultsStmt.run();
setDefaultStmt.run(id);
});
clearAndSet(userId);
return getUserByIdStmt.get(userId) || null;
}
export function publicUser(row) {
if (!row) return null;
return {
@@ -78,5 +108,6 @@ export function publicUser(row) {
email: row.email,
name: row.name,
picture: row.picture,
isDefault: Boolean(row.is_default),
};
}