import { type BotDefaultOncall, type OncallChat } from '../bot-registry.js'; /** * Upsert an oncall binding. Returns whether it was newly created. * * A manual bind is an explicit opt-IN, so it clears any tombstone in * `defaultOncallAutoboundChats` — the tombstone means "auto-bind must not * touch this chat again" (written by unbind and by auto-bind itself), and it * doubles as the provenance list for the leave-oncall release in * {@link setWorkingDirMode}. Clearing it here keeps both consumers honest: a * manually (re-)bound chat is neither auto-bind territory nor releasable as * "auto-bound". A later unbind re-tombstones as before. */ export declare function bindOncall(larkAppId: string, chatId: string, workingDir: string): Promise<{ ok: true; entry: OncallChat; created: boolean; } | { ok: false; reason: string; }>; /** * Unbind oncall for `chatId` and ALWAYS write a tombstone into * `defaultOncallAutoboundChats`. The tombstone protects against the case * where a user manually fiddled with a chat (bound then unbound, or just * unbound) and we then mis-classify it as "new" on the next observation * and re-auto-bind. Treating unbind as "default's one shot is spent" is * symmetric with auto-bind already adding to the same list. * * Idempotent: never errors on "not bound". `wasBound` reports whether an * existing binding was actually removed so callers can phrase UI text * accordingly (the Lark `/oncall unbind` command still wants to say "未绑定" * vs "已解绑"). */ export declare function unbindOncall(larkAppId: string, chatId: string): Promise<{ ok: true; wasBound: boolean; } | { ok: false; reason: string; }>; export declare function getOncallStatus(larkAppId: string, chatId: string): OncallChat | undefined; /** Read the current defaultOncall config + autobound list for a bot. Used by * the dashboard GET route and by the daemon's auto-bind judge. Sync because * it only reads the in-memory snapshot — file-level consistency comes from * the daemon never racing with itself on reads. */ export declare function getBotDefaultOncall(larkAppId: string): { defaultOncall: BotDefaultOncall | undefined; autoboundChats: string[]; }; /** * Persist a defaultOncall change for the given bot. The dashboard PUT route * is the only authorized caller — `since` is server-side authoritative so the * frontend can't backdate the cut-off and accidentally include existing chats. * * `since` is stamped on every enabled save, not just the first transition. * This matches the dashboard copy/requirement and prevents a later workingDir * edit from reaching chats that were first observed before that edit. * * When disabled with an empty `workingDir`, the prior workingDir is preserved * so the UI can round-trip (toggle off → toggle back on) without forcing the * user to retype the path. Disable with a non-empty workingDir overwrites. */ export declare function updateBotDefaultOncall(larkAppId: string, patch: { enabled: boolean; workingDir: string; }): Promise<{ ok: true; defaultOncall: BotDefaultOncall; } | { ok: false; reason: string; }>; /** * Atomically set the per-bot「默认工作目录模式」(dashboard 三选一). The two underlying * fields — `defaultWorkingDir` and `defaultOncall` — are mutually exclusive, so BOTH * are written inside a SINGLE `rmwBotEntry` lock. Doing them as two separate locked * writes (applyConfigField + updateBotDefaultOncall) lets two concurrent saves for * different modes interleave and leave `defaultOncall.enabled` AND `defaultWorkingDir` * both set — an inconsistent state where the UI shows oncall (derived from * `defaultOncall.enabled`) but runtime pins `defaultWorkingDir` (see * `effectiveDefaultWorkingDir`). PR #311 Codex review. * * • 'off' → clear defaultWorkingDir; disable defaultOncall (keep its prior dir * so a later toggle back to oncall round-trips without retyping). * • 'default' → set defaultWorkingDir=dir; disable defaultOncall (keep prior dir). * • 'oncall' → enable defaultOncall(dir) + re-stamp `since`; clear defaultWorkingDir. * * `autoWorktree` only applies to 'default' mode (每个新会话自动建 worktree):写入其中, * 其余模式一律清掉(该开关脱离 defaultWorkingDir 无意义,避免残留脏态)。 * * Caller validates `workingDir` (dir existence) first; it is ignored for 'off'. */ export declare function setWorkingDirMode(larkAppId: string, mode: 'off' | 'default' | 'oncall', workingDir: string, autoWorktree?: boolean): Promise<{ ok: true; defaultOncall: BotDefaultOncall; defaultWorkingDir: string | null; defaultWorkingDirAutoWorktree: boolean; } | { ok: false; reason: string; }>; /** * Auto-bind a chat as part of the defaultOncall flow. Atomically: * 1. RE-CHECK tombstone + existing binding against the freshest on-disk * snapshot. The daemon's fast-path tombstone check is informational — * if a concurrent `unbindOncall` wrote a tombstone between then and * now, the lock-internal view sees it and we skip. * 2. Upsert the oncallChats entry (same shape as manual bindOncall). * 3. Append chatId to defaultOncallAutoboundChats (idempotent). * * Returns `skipped: 'tombstoned'` when the lock-internal tombstone check * trips, `skipped: 'already_bound'` when another writer (manual bind by * the user, or a sibling daemon) bound the chat between the fast-path read * and the lock acquisition. Neither is an error. */ export declare function autoBindOncallFromDefault(larkAppId: string, chatId: string, workingDir: string): Promise<{ ok: true; entry: OncallChat; created: boolean; skipped?: undefined; } | { ok: true; skipped: 'tombstoned' | 'already_bound'; entry?: undefined; created?: undefined; } | { ok: false; reason: string; }>; /** * 前置 auto-bind:dispatcher 在权限判断前调用,保证 oncall 群的首条 @bot 消息 * 不被误判为"无权限"弹授权申请卡。 * * 做了与 daemon 侧一致的快速短路:非群聊 / defaultOncall 未开 / 已在 tombstone 列表 * / chat 已有显式 oncall 绑定 → 立刻 return;否则调 autoBindOncallFromDefault * (内部会在锁内做权威二次校验)。idempotent,daemon spawn 路径二次调用安全。 */ export declare function ensureDefaultOncallBound(larkAppId: string, chatId: string, chatType: 'group' | 'p2p'): Promise; export declare function _readRawConfigSyncForTesting(path: string): any[]; //# sourceMappingURL=oncall-store.d.ts.map