// @bun
// src/sdk/types.ts
class TodosAPIError extends Error {
  status;
  statusText;
  body;
  constructor(message, status, statusText, body) {
    super(message);
    this.status = status;
    this.statusText = statusText;
    this.body = body;
    this.name = "TodosAPIError";
  }
}

class TodosNotFoundError extends TodosAPIError {
  constructor(message, body) {
    super(message, 404, "Not Found", body);
    this.name = "TodosNotFoundError";
  }
}

class TodosConflictError extends TodosAPIError {
  constructor(message, body) {
    super(message, 409, "Conflict", body);
    this.name = "TodosConflictError";
  }
}

class TodosUnauthorizedError extends TodosAPIError {
  constructor(message, body) {
    super(message, 401, "Unauthorized", body);
    this.name = "TodosUnauthorizedError";
  }
}

class TodosRateLimitError extends TodosAPIError {
  retryAfter;
  constructor(message, retryAfter, body) {
    super(message, 429, "Too Many Requests", body);
    this.retryAfter = retryAfter;
    this.name = "TodosRateLimitError";
  }
}

class TodosTimeoutError extends Error {
  ms;
  constructor(ms) {
    super(`Request timed out after ${ms}ms`);
    this.ms = ms;
    this.name = "TodosTimeoutError";
  }
}

// src/lib/config.ts
import { existsSync as existsSync2 } from "fs";
import { dirname, join as join2 } from "path";

// src/lib/sync-utils.ts
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
import { join } from "path";
var HOME = process.env["HOME"] || process.env["USERPROFILE"] || "~";
function getHomeDir() {
  return process.env["HOME"] || process.env["USERPROFILE"] || "~";
}
function getTodosGlobalDir() {
  return join(getHomeDir(), ".hasna", "todos");
}
function readJsonFile(path) {
  try {
    return JSON.parse(readFileSync(path, "utf-8"));
  } catch {
    return null;
  }
}

// src/lib/config.ts
function getConfigPath() {
  return join2(getTodosGlobalDir(), "config.json");
}
var cached = null;
function loadConfig() {
  if (cached)
    return cached;
  if (!existsSync2(getConfigPath())) {
    cached = {};
    return cached;
  }
  const config = readJsonFile(getConfigPath()) || {};
  if (typeof config.sync_agents === "string") {
    config.sync_agents = config.sync_agents.split(",").map((a) => a.trim()).filter(Boolean);
  }
  cached = config;
  return cached;
}
function normalizeApiUrl(value) {
  const trimmed = value?.trim();
  if (!trimmed)
    return null;
  return trimmed.replace(/\/+$/, "");
}
function getLocalApiConfig(env = process.env) {
  const config = loadConfig();
  const envApiUrl = normalizeApiUrl(env["TODOS_URL"]);
  const configApiUrl = normalizeApiUrl(config.apiUrl);
  const apiUrl = envApiUrl ?? configApiUrl;
  const apiKey = env["TODOS_API_KEY"] || config.apiKey || null;
  return {
    apiUrl,
    apiKey,
    source: {
      apiUrl: envApiUrl ? "TODOS_URL" : configApiUrl ? "config" : "none",
      apiKey: env["TODOS_API_KEY"] ? "TODOS_API_KEY" : config.apiKey ? "config" : "none"
    }
  };
}

// src/sdk/client.ts
function buildQuery(params) {
  const search = new URLSearchParams;
  for (const [k, v] of Object.entries(params)) {
    if (v !== undefined)
      search.set(k, Array.isArray(v) ? v.join(",") : String(v));
  }
  const s = search.toString();
  return s ? `?${s}` : "";
}

class TasksResource {
  client;
  constructor(client) {
    this.client = client;
  }
  async list(options) {
    return this.client._get("/api/tasks", buildQuery(options || {}));
  }
  async get(id, options) {
    return this.client._get(`/api/tasks/${id}`, buildQuery(options || {}));
  }
  async getWithRelations(id) {
    return this.client._get(`/api/tasks/${id}`, buildQuery({ format: "full" }));
  }
  async create(data) {
    return this.client._post("/api/tasks", data);
  }
  async update(id, data) {
    return this.client._patch(`/api/tasks/${id}`, data);
  }
  async delete(id) {
    return this.client._delete(`/api/tasks/${id}`);
  }
  async start(id, agentId) {
    return this.client._post(`/api/tasks/${id}/start`, { agent_id: agentId });
  }
  async complete(id, agentId) {
    return this.client._post(`/api/tasks/${id}/complete`, { agent_id: agentId });
  }
  async fail(id, options) {
    return this.client._post(`/api/tasks/${id}/fail`, options || {});
  }
  async logProgress(taskId, message, pctComplete, agentId) {
    return this.client._post(`/api/tasks/${taskId}/progress`, {
      message,
      pct_complete: pctComplete,
      agent_id: agentId
    });
  }
  async getProgress(id, options) {
    return this.client._get(`/api/tasks/${id}/progress`, buildQuery(options || {}));
  }
  async getHistory(id, options) {
    return this.client._get(`/api/tasks/${id}/history`, buildQuery(options || {}));
  }
  async getAttachments(id) {
    return this.client._get(`/api/tasks/${id}/attachments`);
  }
  async status(options) {
    return this.client._get("/api/tasks/status", buildQuery(options || {}));
  }
  async next(options) {
    return this.client._get("/api/tasks/next", buildQuery(options || {}));
  }
  async active(projectId) {
    return this.client._get("/api/tasks/active", projectId ? `?project_id=${projectId}` : "");
  }
  async stale(options) {
    return this.client._get("/api/tasks/stale", buildQuery(options || {}));
  }
  async changedSince(since, projectId, options) {
    return this.client._get("/api/tasks/changed", buildQuery({ since, project_id: projectId, fields: options?.fields }));
  }
  async context(options) {
    const q = buildQuery({
      agent_id: options?.agentId,
      project_id: options?.projectId,
      format: options?.format,
      fields: options?.fields
    });
    const url = `${this.client.baseUrl}/api/tasks/context${q}`;
    const res = await this.client._fetchRaw(url);
    if (!res.ok)
      return options?.format === "json" ? {} : "";
    if (options?.format === "json")
      return res.json();
    return res.text();
  }
  async export(options) {
    const q = buildQuery(options || {});
    const url = `${this.client.baseUrl}/api/tasks/export${q}`;
    const res = await this.client._fetchRaw(url);
    if (!res.ok)
      throw new TodosAPIError("Export failed", res.status, res.statusText, null);
    if (options?.format === "csv")
      return res.text();
    return res.json();
  }
  async bulk(ids, action) {
    return this.client._post("/api/tasks/bulk", { ids, action });
  }
  async claim(agentId, projectId) {
    return this.client._post("/api/tasks/claim", { agent_id: agentId, project_id: projectId });
  }
  async* subscribe(options = {}) {
    const q = buildQuery({
      agent_id: options.agentId,
      project_id: options.projectId,
      events: options.events?.join(",")
    });
    const url = `${this.client.baseUrl}/api/tasks/stream${q}`;
    const resp = await fetch(url);
    if (!resp.ok || !resp.body)
      throw new Error(`SSE connection failed: ${resp.status}`);
    const reader = resp.body.getReader();
    const decoder = new TextDecoder;
    let buffer = "";
    try {
      while (true) {
        const { done, value } = await reader.read();
        if (done)
          break;
        buffer += decoder.decode(value, { stream: true });
        const lines = buffer.split(`
`);
        buffer = lines.pop() || "";
        for (const line of lines) {
          if (line.startsWith("data: ")) {
            try {
              const data = JSON.parse(line.slice(6));
              if (data.type !== "connected")
                yield data;
            } catch {}
          }
        }
      }
    } finally {
      reader.releaseLock();
    }
  }
}

class AgentsResource {
  client;
  constructor(client) {
    this.client = client;
  }
  async list(options) {
    return this.client._get("/api/agents", buildQuery(options || {}));
  }
  async register(data) {
    return this.client._post("/api/agents", data);
  }
  async fullRegister(data) {
    return this.client._post("/api/agents", data);
  }
  async update(id, data) {
    return this.client._patch(`/api/agents/${id}`, data);
  }
  async delete(id) {
    return this.client._delete(`/api/agents/${id}`);
  }
  async bulkDelete(ids) {
    return this.client._post("/api/agents/bulk", { ids, action: "delete" });
  }
  async me(name) {
    return this.client._get("/api/agents/me", `?name=${encodeURIComponent(name)}`);
  }
  async queue(agentId) {
    return this.client._get(`/api/agents/${encodeURIComponent(agentId)}/queue`);
  }
  async team(agentId) {
    return this.client._get(`/api/agents/${encodeURIComponent(agentId)}/team`);
  }
  async orgChart() {
    return this.client._get("/api/org");
  }
}

class ProjectsResource {
  client;
  constructor(client) {
    this.client = client;
  }
  async list(options) {
    return this.client._get("/api/projects", buildQuery(options || {}));
  }
  async create(data) {
    return this.client._post("/api/projects", data);
  }
  async delete(id) {
    return this.client._delete(`/api/projects/${id}`);
  }
  async bulkDelete(ids) {
    return this.client._post("/api/projects/bulk", { ids, action: "delete" });
  }
}

class PlansResource {
  client;
  constructor(client) {
    this.client = client;
  }
  async list(projectId) {
    return this.client._get("/api/plans", projectId ? `?project_id=${projectId}` : "");
  }
  async create(data) {
    return this.client._post("/api/plans", data);
  }
  async get(id) {
    return this.client._get(`/api/plans/${id}`);
  }
  async update(id, data) {
    return this.client._patch(`/api/plans/${id}`, data);
  }
  async delete(id) {
    return this.client._delete(`/api/plans/${id}`);
  }
  async bulkDelete(ids) {
    return this.client._post("/api/plans/bulk", { ids, action: "delete" });
  }
}

class OrgsResource {
  client;
  constructor(client) {
    this.client = client;
  }
  async list() {
    return this.client._get("/api/orgs");
  }
  async create(data) {
    return this.client._post("/api/orgs", data);
  }
  async update(id, data) {
    return this.client._patch(`/api/orgs/${id}`, data);
  }
  async delete(id) {
    return this.client._delete(`/api/orgs/${id}`);
  }
}

class WebhooksResource {
  client;
  constructor(client) {
    this.client = client;
  }
  async list() {
    return this.client._get("/api/webhooks");
  }
  async create(data) {
    return this.client._post("/api/webhooks", data);
  }
  async delete(id) {
    return this.client._delete(`/api/webhooks/${id}`);
  }
}

class TemplatesResource {
  client;
  constructor(client) {
    this.client = client;
  }
  async list() {
    return this.client._get("/api/templates");
  }
  async create(data) {
    return this.client._post("/api/templates", data);
  }
  async delete(id) {
    return this.client._delete(`/api/templates/${id}`);
  }
}

class TodosClient {
  baseUrl;
  timeout;
  apiKey;
  maxRetries;
  retryDelay;
  tasks;
  agents;
  projects;
  plans;
  orgs;
  webhooks;
  templates;
  constructor(options = {}) {
    const localConfig = getLocalApiConfig();
    this.baseUrl = normalizeApiUrl(options.baseUrl) || localConfig.apiUrl || "http://localhost:19427";
    this.timeout = options.timeout ?? 1e4;
    this.apiKey = options.apiKey || localConfig.apiKey;
    this.maxRetries = options.maxRetries ?? 0;
    this.retryDelay = options.retryDelay ?? 1000;
    this.tasks = new TasksResource(this);
    this.agents = new AgentsResource(this);
    this.projects = new ProjectsResource(this);
    this.plans = new PlansResource(this);
    this.orgs = new OrgsResource(this);
    this.webhooks = new WebhooksResource(this);
    this.templates = new TemplatesResource(this);
  }
  static fromEnv(apiKey) {
    return new TodosClient({ apiKey });
  }
  async _fetchRaw(url, init) {
    const controller = new AbortController;
    const timer = setTimeout(() => controller.abort(), this.timeout);
    try {
      const headers = this._buildHeaders(init?.headers);
      return await fetch(url, { ...init, headers, signal: controller.signal });
    } finally {
      clearTimeout(timer);
    }
  }
  _buildHeaders(existing) {
    const headers = { "Content-Type": "application/json" };
    if (this.apiKey)
      headers["x-api-key"] = this.apiKey;
    if (existing) {
      if (existing instanceof Headers) {
        existing.forEach((v, k) => {
          headers[k] = v;
        });
      } else if (Array.isArray(existing)) {
        for (const [k, v] of existing)
          headers[k] = v;
      } else {
        Object.assign(headers, existing);
      }
    }
    return headers;
  }
  async _fetchWithRetry(path, init) {
    let lastError = null;
    const maxAttempts = this.maxRetries + 1;
    for (let attempt = 0;attempt < maxAttempts; attempt++) {
      try {
        return await this._fetch(path, init);
      } catch (e) {
        lastError = e;
        if (e instanceof TodosAPIError && e.status < 500 && e.status !== 429)
          throw e;
        if (e instanceof TodosUnauthorizedError || e instanceof TodosNotFoundError || e instanceof TodosConflictError)
          throw e;
        if (attempt < maxAttempts - 1) {
          const delay = this.retryDelay * Math.pow(2, attempt);
          if (e instanceof TodosRateLimitError) {
            await this._sleep(e.retryAfter * 1000);
          } else {
            await this._sleep(delay);
          }
        }
      }
    }
    throw lastError || new Error("Request failed after retries");
  }
  async _fetch(path, init) {
    const controller = new AbortController;
    const timer = setTimeout(() => controller.abort(), this.timeout);
    try {
      const url = `${this.baseUrl}${path}`;
      const headers = this._buildHeaders(init?.headers);
      const res = await fetch(url, { ...init, headers, signal: controller.signal });
      if (!res.ok) {
        const body = await res.json().catch(() => ({ error: res.statusText }));
        const message = body.error || `HTTP ${res.status}: ${res.statusText}`;
        if (res.status === 401)
          throw new TodosUnauthorizedError(message, body);
        if (res.status === 404)
          throw new TodosNotFoundError(message, body);
        if (res.status === 409)
          throw new TodosConflictError(message, body);
        if (res.status === 429) {
          const retryAfter = parseInt(res.headers.get("retry-after") || "60", 10);
          throw new TodosRateLimitError(message, retryAfter, body);
        }
        throw new TodosAPIError(message, res.status, res.statusText, body);
      }
      const contentLength = res.headers.get("content-length");
      if (contentLength === "0" || contentLength === "4")
        return null;
      return res.json();
    } catch (e) {
      if (e instanceof DOMException && e.name === "AbortError") {
        throw new TodosTimeoutError(this.timeout);
      }
      throw e;
    } finally {
      clearTimeout(timer);
    }
  }
  async _get(path, query = "") {
    return this._fetchWithRetry(`${path}${query}`);
  }
  async _post(path, body) {
    return this._fetchWithRetry(path, {
      method: "POST",
      body: body ? JSON.stringify(body) : undefined
    });
  }
  async _patch(path, body) {
    return this._fetchWithRetry(path, {
      method: "PATCH",
      body: JSON.stringify(body)
    });
  }
  async _delete(path) {
    return this._fetchWithRetry(path, { method: "DELETE" });
  }
  _sleep(ms) {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }
  async getHealth() {
    return this._get("/api/health");
  }
  async isAlive() {
    try {
      await this._get("/api/stats");
      return true;
    } catch {
      return false;
    }
  }
  async getStats() {
    return this._get("/api/stats");
  }
  async getReport(options) {
    return this._get("/api/report", buildQuery({ days: options?.days, project_id: options?.projectId }));
  }
  async doctor() {
    return this._get("/api/doctor");
  }
  async activity(limit) {
    return this._get("/api/activity", limit ? `?limit=${limit}` : "");
  }
  async listTasks(filter = {}) {
    return this.tasks.list(filter);
  }
  async getTask(id, options) {
    return this.tasks.get(id, options);
  }
  async createTask(data) {
    return this.tasks.create(data);
  }
  async updateTask(id, data) {
    return this.tasks.update(id, data);
  }
  async deleteTask(id) {
    await this.tasks.delete(id);
  }
  async startTask(id, agentId) {
    return this.tasks.start(id, agentId);
  }
  async completeTask(id, agentId) {
    return this.tasks.complete(id, agentId);
  }
  async failTask(id, options = {}) {
    return this.tasks.fail(id, options);
  }
  async logProgress(taskId, message, pctComplete, agentId) {
    return this.tasks.logProgress(taskId, message, pctComplete, agentId);
  }
  async getStatus(projectId, agentId) {
    return this.tasks.status({ project_id: projectId, agent_id: agentId });
  }
  async getActiveWork(projectId) {
    const res = await this.tasks.active(projectId);
    return res.active;
  }
  async getTasksChangedSince(since, projectId) {
    return this.tasks.changedSince(since, projectId);
  }
  async getStaleTasks(minutes, projectId) {
    return this.tasks.stale({ minutes, project_id: projectId });
  }
  async getContext(options = {}) {
    return this.tasks.context(options);
  }
  async exportTasks(filter = {}) {
    return this.tasks.export(filter);
  }
  async claimNextTask(agentId, projectId) {
    return this.tasks.claim(agentId, projectId);
  }
  async getTaskHistory(id, options) {
    return this.tasks.getHistory(id, options);
  }
  async getTaskAttachments(id) {
    return this.tasks.getAttachments(id);
  }
  async getTaskProgress(id, options) {
    return this.tasks.getProgress(id, options);
  }
  async* subscribeToStream(options = {}) {
    yield* this.tasks.subscribe(options);
  }
  async getProjects() {
    return this.projects.list();
  }
}
function createClient(options) {
  return new TodosClient(options);
}
export {
  createClient,
  TodosUnauthorizedError,
  TodosTimeoutError,
  TodosRateLimitError,
  TodosNotFoundError,
  TodosConflictError,
  TodosClient,
  TodosAPIError
};
