/** * daemon/todoist-ingress.ts — granting a project the right to reach a session. * * The allowlist began as one environment variable of opaque project ids, read * once at daemon start. That is the wrong shape for something you change while * using the system: creating a Todoist project and then editing a file and * restarting a daemon is not a workflow anyone will follow, and a project that * looks ready but routes nowhere fails exactly the way this codebase keeps * trying to stop things failing — silently, with everything appearing fine. * * So grants live here too, in a store the daemon re-reads, and every change is * an operation with an audit record rather than a file edit. The environment * variable still works and still wins nothing: the two are merged, so an * existing setup keeps behaving exactly as it did. * * What does NOT change is that the list stays explicit. A task filed into a * project becomes an instruction a session runs with the user's full rights, * and Todoist's payload documents that the initiator may be a collaborator on * a shared project. Granting ingress must be a decision. It is now a decision * that is recorded. */ import type { WebhookConfig } from "./todoist-webhook.js"; export interface IngressGrant { projectId: string; /** * Does this grant cover the project's descendants? * * Opt-in per grant, deliberately. A sub-project is a folder, not a second * owner: someone organising their work into "Task Bus / Archive" has not * made a decision about execution ingress, and before this * existed those tasks were refused with "not an ingress project" — silently, * and exactly when they tidied up. * * Still not implicit inheritance: granting a subtree is a decision, made * once, and the residual risk is worth stating. A project shared with you and * later moved under a granted root inherits execution rights nobody * considered for it. Grant subtrees to roots you own. */ subtree?: boolean; /** Human-readable, for the CLI and for reading the file a month from now. */ projectName?: string; /** Session that owns work filed here. Unset falls through to the default. */ owner?: string; grantedAt: string; } export declare function listGrants(): IngressGrant[]; /** * Which project belongs to this session. * * Exists because a session knows itself by its alias — `task-bus` — while * the project a human made for it is called `Task Bus`. Asked to file a * task "in my project", a session that compares those literally finds nothing * and creates a second project named after the alias. Two projects then look * like one, work lands in whichever the session picked, and the human watches * the other. Ask here instead of guessing from a name. * * Owner matching folds separators, for the same reason session matching does. */ export declare function projectForOwner(owner: string): IngressGrant | undefined; /** Grant a project the right to reach a session. Idempotent. */ export declare function grantIngress(projectId: string, opts?: { owner?: string; projectName?: string; subtree?: boolean; }): IngressGrant; /** Revoke a grant. Returns whether anything was there to revoke. */ export declare function revokeIngress(projectId: string): boolean; /** * Merge stored grants into a config read from the environment. * * Called per request rather than at startup: the point of the store is that a * grant takes effect when it is made, not when the daemon is next restarted. * Returns a new object — the caller's config is never mutated. */ export declare function applyGrants(cfg: WebhookConfig): WebhookConfig; /** * Allow a project reachable through an ancestor that granted its subtree. * * Returns a config with that project added, so everything downstream — the * boundary check, the owner lookup, comment routing via a parent task — works * unchanged. The owner is inherited from the nearest granting ancestor, because * a folder is not a second owner. * * Returns the config untouched when nothing applies, so the ordinary path costs * nothing. */ export declare function expandThroughSubtree(cfg: WebhookConfig, projectId: string, ancestors: string[], /** * The project's own name, and the owners a name may resolve to. * * A granted root often has no owner of its own — "Claude 🤖" is a container, * not a session — and its children are named after the sessions they belong * to: Home, SL, Whazaa. Without this, every child of such a root inherits * nothing and falls through to the default owner, so tasks filed into * "Claude 🤖 / Home" arrive at whichever session the default names. That was * the symptom: three projects created in one afternoon, all silently * unreachable. * * The name is read from the project tree BY ID. Todoist's project search * returns nothing for names containing emoji, so anything that looked a * project up by name would report one that plainly exists as absent. */ self?: { name?: string; known?: Iterable; }): WebhookConfig; //# sourceMappingURL=todoist-ingress.d.ts.map