@hasna/todos 0.15.52 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/CHANGELOG.md +1884 -0
  2. package/README.md +314 -37
  3. package/dist/ai-tools.d.ts.map +1 -1
  4. package/dist/ai.d.ts.map +1 -1
  5. package/dist/cli/cloud-router.d.ts +99 -51
  6. package/dist/cli/cloud-router.d.ts.map +1 -1
  7. package/dist/cli/commands/agent-commands.d.ts.map +1 -1
  8. package/dist/cli/commands/config-serve-commands.d.ts.map +1 -1
  9. package/dist/cli/commands/delegate.d.ts.map +1 -1
  10. package/dist/cli/commands/environment-snapshots.d.ts.map +1 -1
  11. package/dist/cli/commands/machines-api.d.ts +4 -0
  12. package/dist/cli/commands/machines-api.d.ts.map +1 -0
  13. package/dist/cli/commands/machines.d.ts.map +1 -1
  14. package/dist/cli/commands/mcp-hooks-commands.d.ts.map +1 -1
  15. package/dist/cli/commands/plan-commands.d.ts +3 -0
  16. package/dist/cli/commands/plan-commands.d.ts.map +1 -0
  17. package/dist/cli/commands/plan-template-commands.d.ts.map +1 -1
  18. package/dist/cli/commands/pr-group-commands.d.ts.map +1 -1
  19. package/dist/cli/commands/project-commands.d.ts.map +1 -1
  20. package/dist/cli/commands/query-commands.d.ts.map +1 -1
  21. package/dist/cli/commands/storage-commands.d.ts.map +1 -1
  22. package/dist/cli/commands/task-list-commands.d.ts +3 -0
  23. package/dist/cli/commands/task-list-commands.d.ts.map +1 -0
  24. package/dist/cli/commands/template-commands.d.ts +3 -0
  25. package/dist/cli/commands/template-commands.d.ts.map +1 -0
  26. package/dist/cli/helpers.d.ts.map +1 -1
  27. package/dist/cli/index.js +15128 -11093
  28. package/dist/cli/machine-api.d.ts +7 -0
  29. package/dist/cli/machine-api.d.ts.map +1 -0
  30. package/dist/cli/plan-read-api.d.ts +6 -0
  31. package/dist/cli/plan-read-api.d.ts.map +1 -0
  32. package/dist/cli/project-api.d.ts +119 -0
  33. package/dist/cli/project-api.d.ts.map +1 -0
  34. package/dist/cli/stage-a.d.ts +34 -4
  35. package/dist/cli/stage-a.d.ts.map +1 -1
  36. package/dist/cli/task-query-api.d.ts +21 -0
  37. package/dist/cli/task-query-api.d.ts.map +1 -0
  38. package/dist/cli/template-api.d.ts +7 -0
  39. package/dist/cli/template-api.d.ts.map +1 -0
  40. package/dist/contracts.js +835 -265
  41. package/dist/db/builtin-templates.d.ts +2 -33
  42. package/dist/db/builtin-templates.d.ts.map +1 -1
  43. package/dist/db/database.d.ts.map +1 -1
  44. package/dist/db/handoffs.d.ts.map +1 -1
  45. package/dist/db/machines.d.ts.map +1 -1
  46. package/dist/db/migrations.d.ts.map +1 -1
  47. package/dist/db/plans.d.ts.map +1 -1
  48. package/dist/db/retrospectives.d.ts.map +1 -1
  49. package/dist/db/schema.d.ts.map +1 -1
  50. package/dist/db/task-lists.d.ts.map +1 -1
  51. package/dist/db/task-runs.d.ts +2 -0
  52. package/dist/db/task-runs.d.ts.map +1 -1
  53. package/dist/index.d.ts +2 -2
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +3862 -1715
  56. package/dist/lib/assignee-validation.d.ts.map +1 -1
  57. package/dist/lib/builtin-template-library.d.ts +32 -0
  58. package/dist/lib/builtin-template-library.d.ts.map +1 -0
  59. package/dist/lib/cli-reference.d.ts.map +1 -1
  60. package/dist/lib/config.d.ts +10 -13
  61. package/dist/lib/config.d.ts.map +1 -1
  62. package/dist/lib/db-backup.d.ts +1 -1
  63. package/dist/lib/db-backup.d.ts.map +1 -1
  64. package/dist/lib/env.d.ts +18 -0
  65. package/dist/lib/env.d.ts.map +1 -0
  66. package/dist/lib/environment-snapshots.d.ts.map +1 -1
  67. package/dist/lib/gatherer.d.ts.map +1 -1
  68. package/dist/lib/headless-boundaries.d.ts +4 -3
  69. package/dist/lib/headless-boundaries.d.ts.map +1 -1
  70. package/dist/lib/local-encryption.d.ts +12 -0
  71. package/dist/lib/local-encryption.d.ts.map +1 -1
  72. package/dist/lib/local-opt-in.d.ts +90 -0
  73. package/dist/lib/local-opt-in.d.ts.map +1 -0
  74. package/dist/lib/paths.d.ts +9 -0
  75. package/dist/lib/paths.d.ts.map +1 -1
  76. package/dist/lib/plan-artifacts.d.ts +1 -0
  77. package/dist/lib/plan-artifacts.d.ts.map +1 -1
  78. package/dist/lib/plan-client-boundary.d.ts +2 -0
  79. package/dist/lib/plan-client-boundary.d.ts.map +1 -0
  80. package/dist/lib/plan-schedule.d.ts +6 -0
  81. package/dist/lib/plan-schedule.d.ts.map +1 -0
  82. package/dist/lib/project-panel.d.ts +6 -0
  83. package/dist/lib/project-panel.d.ts.map +1 -1
  84. package/dist/lib/project-receipt.d.ts +3 -0
  85. package/dist/lib/project-receipt.d.ts.map +1 -0
  86. package/dist/lib/sandbox-profiles.d.ts.map +1 -1
  87. package/dist/lib/shared-plan-artifacts.d.ts +14 -0
  88. package/dist/lib/shared-plan-artifacts.d.ts.map +1 -0
  89. package/dist/lib/sync.d.ts.map +1 -1
  90. package/dist/lib/task-list-client-boundary.d.ts +3 -0
  91. package/dist/lib/task-list-client-boundary.d.ts.map +1 -0
  92. package/dist/lib/task-routing.d.ts.map +1 -1
  93. package/dist/lib/template-client-boundary.d.ts +2 -0
  94. package/dist/lib/template-client-boundary.d.ts.map +1 -0
  95. package/dist/mcp/index.d.ts +1 -0
  96. package/dist/mcp/index.d.ts.map +1 -1
  97. package/dist/mcp/index.js +45271 -40452
  98. package/dist/mcp/plan-api.d.ts +6 -0
  99. package/dist/mcp/plan-api.d.ts.map +1 -0
  100. package/dist/mcp/remote-authority.d.ts +49 -0
  101. package/dist/mcp/remote-authority.d.ts.map +1 -0
  102. package/dist/mcp/task-coordination-api.d.ts +15 -0
  103. package/dist/mcp/task-coordination-api.d.ts.map +1 -0
  104. package/dist/mcp/task-list-api.d.ts +8 -0
  105. package/dist/mcp/task-list-api.d.ts.map +1 -0
  106. package/dist/mcp/token-utils.d.ts.map +1 -1
  107. package/dist/mcp/tools/environment-snapshots.d.ts.map +1 -1
  108. package/dist/mcp/tools/machines.d.ts.map +1 -1
  109. package/dist/mcp/tools/task-project-tools.d.ts.map +1 -1
  110. package/dist/mcp/tools/task-resources.d.ts.map +1 -1
  111. package/dist/mcp.d.ts +2 -2
  112. package/dist/mcp.d.ts.map +1 -1
  113. package/dist/mcp.js +44 -18
  114. package/dist/project-registration.js +2829 -556
  115. package/dist/registry.js +836 -266
  116. package/dist/release-provenance.json +6 -6
  117. package/dist/sdk/client.d.ts +51 -4
  118. package/dist/sdk/client.d.ts.map +1 -1
  119. package/dist/sdk/index.d.ts +2 -0
  120. package/dist/sdk/index.d.ts.map +1 -1
  121. package/dist/sdk/index.js +1209 -1080
  122. package/dist/sdk/resolve.d.ts +91 -0
  123. package/dist/sdk/resolve.d.ts.map +1 -0
  124. package/dist/sdk/v1.generated.d.ts +219 -3
  125. package/dist/sdk/v1.generated.d.ts.map +1 -1
  126. package/dist/server/auth-posture.d.ts +66 -7
  127. package/dist/server/auth-posture.d.ts.map +1 -1
  128. package/dist/server/client-ip.d.ts +39 -0
  129. package/dist/server/client-ip.d.ts.map +1 -0
  130. package/dist/server/cloud.d.ts +2 -1
  131. package/dist/server/cloud.d.ts.map +1 -1
  132. package/dist/server/index.d.ts +1 -1
  133. package/dist/server/index.js +20556 -15541
  134. package/dist/server/openapi.d.ts +1341 -83
  135. package/dist/server/openapi.d.ts.map +1 -1
  136. package/dist/server/port.d.ts +2 -2
  137. package/dist/server/port.d.ts.map +1 -1
  138. package/dist/server/routes.d.ts +3 -6
  139. package/dist/server/routes.d.ts.map +1 -1
  140. package/dist/server/serve.d.ts +2 -6
  141. package/dist/server/serve.d.ts.map +1 -1
  142. package/dist/server/v1.d.ts +3 -1
  143. package/dist/server/v1.d.ts.map +1 -1
  144. package/dist/storage/atomic-project-migration.d.ts +53 -0
  145. package/dist/storage/atomic-project-migration.d.ts.map +1 -0
  146. package/dist/storage/index.d.ts +3 -1
  147. package/dist/storage/index.d.ts.map +1 -1
  148. package/dist/storage/interfaces.d.ts +61 -0
  149. package/dist/storage/interfaces.d.ts.map +1 -1
  150. package/dist/storage/machine-registry.d.ts +36 -0
  151. package/dist/storage/machine-registry.d.ts.map +1 -0
  152. package/dist/storage/postgres-adapter.d.ts.map +1 -1
  153. package/dist/storage/postgres-sync.d.ts +2 -2
  154. package/dist/storage/postgres-sync.d.ts.map +1 -1
  155. package/dist/storage/run-artifact-creation-upload.d.ts +50 -0
  156. package/dist/storage/run-artifact-creation-upload.d.ts.map +1 -0
  157. package/dist/storage/s3-artifact-sync.d.ts +18 -0
  158. package/dist/storage/s3-artifact-sync.d.ts.map +1 -1
  159. package/dist/storage/sqlite-snapshot.d.ts +2 -0
  160. package/dist/storage/sqlite-snapshot.d.ts.map +1 -1
  161. package/dist/storage.d.ts +2 -2
  162. package/dist/storage.d.ts.map +1 -1
  163. package/dist/storage.js +2695 -322
  164. package/dist/task-manifest/postgres.d.ts +1 -1
  165. package/dist/task-manifest/sqlite.d.ts +1 -1
  166. package/dist/task-manifest.js +244 -73
  167. package/dist/task-subtree-transfer.js +198 -4
  168. package/dist/testing.d.ts +90 -29
  169. package/dist/testing.d.ts.map +1 -1
  170. package/dist/testing.js +34 -10
  171. package/dist/types/index.d.ts +40 -4
  172. package/dist/types/index.d.ts.map +1 -1
  173. package/docs/PLAN_API.md +93 -0
  174. package/docs/TASK_LIST_API.md +58 -0
  175. package/docs/TASK_QUERY_API.md +14 -0
  176. package/docs/TEMPLATE_API.md +18 -0
  177. package/docs/native-storage.md +405 -0
  178. package/package.json +18 -15
  179. package/postinstall.js +52 -1
  180. package/dashboard/dist/assets/index-BNQ4gJua.js +0 -342
  181. package/dashboard/dist/assets/index-DjzvHUWt.css +0 -1
  182. package/dashboard/dist/index.html +0 -13
  183. package/dashboard/dist/logo.jpg +0 -0
package/dist/sdk/index.js CHANGED
@@ -52,1292 +52,1420 @@ class TodosTimeoutError extends Error {
52
52
  }
53
53
  }
54
54
 
55
- // src/lib/config.ts
56
- import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
57
- import { dirname, join as join3 } from "path";
55
+ // src/sdk/resolve.ts
56
+ import {
57
+ ClientTransportConfigurationError,
58
+ resolveClientTransport,
59
+ resolveCredential
60
+ } from "@hasna/contracts/client";
58
61
 
59
- // src/lib/sync-utils.ts
60
- import { existsSync as existsSync2, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
61
- import { homedir as homedir3 } from "os";
62
-
63
- // src/lib/paths.ts
64
- import { existsSync } from "fs";
65
- import { homedir as homedir2 } from "os";
66
- import { join as join2, resolve } from "path";
67
-
68
- // node_modules/.bun/@hasna+paths@0.1.0/node_modules/@hasna/paths/dist/index.js
69
- import { homedir } from "os";
70
- import { join } from "path";
71
- var KIND_ENV = {
72
- config: "HASNA_CONFIG_HOME",
73
- data: "HASNA_DATA_HOME",
74
- state: "HASNA_STATE_HOME",
75
- cache: "HASNA_CACHE_HOME"
76
- };
77
- var APP_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
78
- function assertApp(app) {
79
- if (typeof app !== "string" || app.length === 0) {
80
- throw new TypeError("paths: app must be a non-empty string");
81
- }
82
- if (!APP_SLUG_RE.test(app)) {
83
- throw new TypeError(`paths: invalid app slug "${app}" \u2014 expected lowercase kebab-case ([a-z0-9]+(-[a-z0-9]+)*)`);
84
- }
85
- }
86
- function envOf(options) {
87
- return options.env ?? process.env;
88
- }
89
- function envValue(options, kind) {
90
- const value = envOf(options)[KIND_ENV[kind]];
91
- return typeof value === "string" && value.length > 0 ? value : undefined;
92
- }
93
- function isMacOS(platform) {
94
- return platform === "darwin";
95
- }
96
- function baseDir(kind, options) {
97
- const override = envValue(options, kind);
98
- if (override)
99
- return override;
100
- const home = options.home ?? homedir();
101
- const platform = options.platform ?? process.platform;
102
- if (isMacOS(platform)) {
103
- switch (kind) {
104
- case "config":
105
- case "data":
106
- return join(home, "Library", "Application Support", "Hasna");
107
- case "cache":
108
- return join(home, "Library", "Caches", "Hasna");
109
- case "state":
110
- return join(home, "Library", "Logs", "Hasna");
111
- }
112
- }
113
- switch (kind) {
114
- case "config":
115
- return join(home, ".config", "hasna");
116
- case "data":
117
- return join(home, ".local", "share", "hasna");
118
- case "state":
119
- return join(home, ".local", "state", "hasna");
120
- case "cache":
121
- return join(home, ".cache", "hasna");
62
+ // src/project-registration/types.ts
63
+ class TodosProjectRegistrationError extends Error {
64
+ code;
65
+ details;
66
+ constructor(code, message, details = {}) {
67
+ super(message);
68
+ this.code = code;
69
+ this.details = details;
70
+ this.name = "TodosProjectRegistrationError";
122
71
  }
123
72
  }
124
- function resolvePath(kind, options) {
125
- assertApp(options.app);
126
- const appSegment = options.internal === true ? join("internal", options.app) : options.app;
127
- return join(baseDir(kind, options), appSegment);
128
- }
129
- function dataDir(options) {
130
- return resolvePath("data", options);
131
- }
132
73
 
133
- // src/lib/paths.ts
134
- function effectiveHome(env = process.env) {
135
- return env.HOME || env.USERPROFILE || homedir2();
136
- }
137
- function legacyHomeDir(env = process.env) {
138
- return join2(effectiveHome(env), ".hasna", "todos");
139
- }
140
- function resolverHome(env = process.env) {
141
- return dataDir({ app: "todos", home: effectiveHome(env), env });
142
- }
143
- function adoptResolverHome(resolved, env = process.env) {
144
- const dataOverride = env.HASNA_DATA_HOME;
145
- if (typeof dataOverride === "string" && dataOverride.trim().length > 0)
146
- return true;
147
- return existsSync(join2(resolved, "todos.db")) || existsSync(join2(resolved, "config.json"));
74
+ // src/project-registration/adoption-validation.ts
75
+ var VALIDATION_KEYS = [
76
+ "valid",
77
+ "resource_kind",
78
+ "target_id",
79
+ "source_receipt_id",
80
+ "accepted_receipt_id",
81
+ "source_outcome",
82
+ "created_at",
83
+ "current_revision",
84
+ "accepted_result_digest"
85
+ ];
86
+ function isRecord(value) {
87
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
148
88
  }
149
- function getTodosDir(env = process.env) {
150
- const resolved = resolverHome(env);
151
- return resolve(adoptResolverHome(resolved, env) ? resolved : legacyHomeDir(env));
89
+ function isNonEmptyString(value) {
90
+ return typeof value === "string" && value.length > 0;
152
91
  }
153
-
154
- // src/lib/sync-utils.ts
155
- function getHomeDir() {
156
- return process.env["HOME"] || process.env["USERPROFILE"] || homedir3();
92
+ function hasExactKeys(value, expected) {
93
+ const actual = Object.keys(value).sort();
94
+ const wanted = [...expected].sort();
95
+ return actual.length === wanted.length && actual.every((key, index) => key === wanted[index]);
157
96
  }
158
- var HOME = getHomeDir();
159
- function getTodosGlobalDir() {
160
- return getTodosDir();
97
+ function adoptionRejected(message) {
98
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_ADOPTION_REJECTED", `TODOS_PROJECT_REGISTRATION_ADOPTION_REJECTED: ${message}`);
161
99
  }
162
- function readJsonFile(path) {
163
- try {
164
- return JSON.parse(readFileSync(path, "utf-8"));
165
- } catch {
166
- return null;
100
+ function assertTodosPriorRegistrationAdoptionValidationEnvelope(value, input) {
101
+ if (!isRecord(input) || !hasExactKeys(input, [
102
+ "source_request",
103
+ "source_receipt",
104
+ "current_record"
105
+ ])) {
106
+ adoptionRejected("prior-adoption validation input is incomplete");
167
107
  }
108
+ const request = input["source_request"];
109
+ const receipt = input["source_receipt"];
110
+ const current = input["current_record"];
111
+ if (!isRecord(request) || !isRecord(receipt) || !isRecord(current)) {
112
+ adoptionRejected("prior-adoption validation input records are incomplete");
113
+ }
114
+ const resourceKind = request["resource_kind"];
115
+ const sourceOutcome = receipt["outcome"];
116
+ const acceptedReceiptId = sourceOutcome === "accepted" ? receipt["receipt_id"] : sourceOutcome === "duplicate_of_accepted" ? receipt["duplicate_of_receipt_id"] : null;
117
+ if (resourceKind !== "project" && resourceKind !== "task_list" || request["direction"] !== "forward" || sourceOutcome !== "accepted" && sourceOutcome !== "duplicate_of_accepted" || !isNonEmptyString(acceptedReceiptId) || !isNonEmptyString(receipt["receipt_id"]) || !isNonEmptyString(receipt["target_id"]) || !isNonEmptyString(receipt["result_revision"]) || !isNonEmptyString(receipt["result_digest"]) || !isNonEmptyString(current["id"]) || !isNonEmptyString(current["created_at"]) || !isNonEmptyString(current["updated_at"]) || receipt["authority"] !== "todos" || receipt["route"] !== request["authority_route"] || receipt["package_version"] !== request["package_version"] || receipt["authority_id"] !== request["authority_id"] || receipt["tenant_id"] !== request["tenant_id"] || receipt["corpus_id"] !== request["corpus_id"] || receipt["operation_id"] !== request["operation_id"] || receipt["step_id"] !== request["step_id"] || receipt["resource_kind"] !== resourceKind || receipt["direction"] !== "forward" || receipt["idempotency_key"] !== request["idempotency_key"] || receipt["request_digest"] !== request["request_digest"] || receipt["precondition_digest"] !== request["precondition_digest"] || receipt["accepted_receipt_id"] !== null || receipt["target_id"] !== current["id"] || receipt["result_revision"] !== current["created_at"]) {
118
+ adoptionRejected("prior-adoption validation input does not carry one complete accepted receipt and current target incarnation");
119
+ }
120
+ if (sourceOutcome === "accepted" && receipt["duplicate_of_receipt_id"] !== null || sourceOutcome === "duplicate_of_accepted" && receipt["duplicate_of_receipt_id"] !== acceptedReceiptId) {
121
+ adoptionRejected("prior-adoption validation source receipt lineage is incomplete");
122
+ }
123
+ if (!isRecord(value) || !hasExactKeys(value, ["validation"]) || !isRecord(value["validation"]) || !hasExactKeys(value["validation"], VALIDATION_KEYS)) {
124
+ adoptionRejected("prior-adoption validation response envelope is incomplete");
125
+ }
126
+ const validation = value["validation"];
127
+ if (validation["valid"] !== true || validation["resource_kind"] !== resourceKind || validation["target_id"] !== current["id"] || validation["source_receipt_id"] !== receipt["receipt_id"] || validation["accepted_receipt_id"] !== acceptedReceiptId || validation["source_outcome"] !== sourceOutcome || validation["created_at"] !== current["created_at"] || validation["current_revision"] !== current["updated_at"] || validation["accepted_result_digest"] !== receipt["result_digest"]) {
128
+ adoptionRejected("prior-adoption validation response does not prove the exact accepted receipt and current target");
129
+ }
130
+ return validation;
168
131
  }
169
132
 
170
- // src/lib/config.ts
171
- function getConfigPath() {
172
- return join3(getTodosGlobalDir(), "config.json");
173
- }
174
- var cached = null;
175
- function loadConfig() {
176
- if (cached)
177
- return cached;
178
- if (!existsSync3(getConfigPath())) {
179
- cached = {};
180
- return cached;
181
- }
182
- const config = readJsonFile(getConfigPath()) || {};
183
- if (typeof config.sync_agents === "string") {
184
- config.sync_agents = config.sync_agents.split(",").map((a) => a.trim()).filter(Boolean);
185
- }
186
- cached = config;
187
- return cached;
188
- }
189
- function normalizeApiUrl(value) {
190
- const trimmed = value?.trim();
191
- if (!trimmed)
192
- return null;
193
- return trimmed.replace(/\/+$/, "");
194
- }
195
- function getLocalApiConfig(env = process.env) {
196
- const config = loadConfig();
197
- const envApiUrl = normalizeApiUrl(env["TODOS_URL"]);
198
- const configApiUrl = normalizeApiUrl(config.apiUrl);
199
- const apiUrl = envApiUrl ?? configApiUrl;
200
- const apiKey = env["TODOS_API_KEY"] || config.apiKey || null;
201
- return {
202
- apiUrl,
203
- apiKey,
204
- source: {
205
- apiUrl: envApiUrl ? "TODOS_URL" : configApiUrl ? "config" : "none",
206
- apiKey: env["TODOS_API_KEY"] ? "TODOS_API_KEY" : config.apiKey ? "config" : "none"
207
- }
208
- };
209
- }
210
-
211
- // src/sdk/client.ts
212
- function buildQuery(params) {
213
- const search = new URLSearchParams;
214
- for (const [k, v] of Object.entries(params)) {
215
- if (v !== undefined)
216
- search.set(k, Array.isArray(v) ? v.join(",") : String(v));
133
+ // src/sdk/v1.generated.ts
134
+ class ApiError extends Error {
135
+ status;
136
+ body;
137
+ constructor(status, message, body) {
138
+ super(message);
139
+ this.status = status;
140
+ this.body = body;
141
+ this.name = "ApiError";
217
142
  }
218
- const s = search.toString();
219
- return s ? `?${s}` : "";
220
143
  }
221
144
 
222
- class TasksResource {
223
- client;
224
- constructor(client) {
225
- this.client = client;
145
+ class TodosV1Client {
146
+ baseUrl;
147
+ apiKey;
148
+ fetchImpl;
149
+ baseHeaders;
150
+ constructor(options) {
151
+ if (!options.baseUrl)
152
+ throw new Error("TodosV1Client requires a baseUrl.");
153
+ this.baseUrl = options.baseUrl.replace(/\/$/, "");
154
+ this.apiKey = options.apiKey;
155
+ this.fetchImpl = options.fetch ?? globalThis.fetch;
156
+ this.baseHeaders = options.headers ?? {};
226
157
  }
227
- async list(options) {
228
- return this.client._get("/api/tasks", buildQuery(options || {}));
158
+ async request(method, path, opts) {
159
+ const url = new URL(this.baseUrl + path);
160
+ if (opts.query) {
161
+ for (const [key, value] of Object.entries(opts.query)) {
162
+ if (value === undefined || value === null)
163
+ continue;
164
+ if (Array.isArray(value)) {
165
+ for (const item of value) {
166
+ if (item !== undefined && item !== null)
167
+ url.searchParams.append(key, String(item));
168
+ }
169
+ } else {
170
+ url.searchParams.set(key, String(value));
171
+ }
172
+ }
173
+ }
174
+ const headers = { Accept: "application/json", ...this.baseHeaders, ...opts.init?.headers };
175
+ if (this.apiKey)
176
+ headers["x-api-key"] = this.apiKey;
177
+ let payload;
178
+ if (opts.body !== undefined) {
179
+ headers["Content-Type"] = "application/json";
180
+ payload = JSON.stringify(opts.body);
181
+ }
182
+ const response = await this.fetchImpl(url.toString(), { ...opts.init, method, headers, body: payload });
183
+ const text = await response.text();
184
+ const data = text ? (() => {
185
+ try {
186
+ return JSON.parse(text);
187
+ } catch {
188
+ return text;
189
+ }
190
+ })() : undefined;
191
+ if (!response.ok) {
192
+ throw new ApiError(response.status, `${method} ${path} failed: ${response.status}`, data);
193
+ }
194
+ return data;
229
195
  }
230
- async get(id, options) {
231
- return this.client._get(`/api/tasks/${id}`, buildQuery(options || {}));
196
+ async importSnapshot(body, init) {
197
+ if (body.machines !== undefined && !Array.isArray(body.machines))
198
+ throw new Error("Invalid machine snapshot array; no snapshot was posted");
199
+ if (body.machines?.length) {
200
+ let capability;
201
+ try {
202
+ capability = await this.listMachines(init);
203
+ } catch {
204
+ throw new Error("REMOTE_API_INCOMPATIBLE: machine import requires an upgraded Todos server; no snapshot was posted");
205
+ }
206
+ if (capability?.schema_version !== 2 || !Array.isArray(capability.machines) || !capability.authority || typeof capability.authority.tenant_id !== "string" || !capability.authority.tenant_id || typeof capability.authority.kid !== "string" || !capability.authority.kid)
207
+ throw new Error("REMOTE_API_INCOMPATIBLE: machine registry capability is missing; no snapshot was posted");
208
+ if (body.expected_machine_authority && (body.expected_machine_authority.kid !== capability.authority.kid || body.expected_machine_authority.tenant_id !== capability.authority.tenant_id))
209
+ throw new Error("Machine migration authority changed before POST; no snapshot was posted");
210
+ body = { ...body, expected_machine_authority: { ...capability.authority } };
211
+ }
212
+ const result = await this.request("POST", `/v1/import`, {
213
+ body,
214
+ query: undefined,
215
+ init
216
+ });
217
+ if (body.machines?.length && (result.machine_authority?.kid !== body.expected_machine_authority?.kid || result.machine_authority?.tenant_id !== body.expected_machine_authority?.tenant_id))
218
+ throw new Error("Machine receipt authority changed; preserve the source and inspect before retrying");
219
+ return result;
232
220
  }
233
- async getWithRelations(id) {
234
- return this.client._get(`/api/tasks/${id}`, buildQuery({ format: "full" }));
221
+ async listMachines(init) {
222
+ return this.request("GET", `/v1/machines`, {
223
+ body: undefined,
224
+ query: undefined,
225
+ init
226
+ });
235
227
  }
236
- async create(data) {
237
- return this.client._post("/api/tasks", data);
228
+ async mutateMachineRegistry(body, init) {
229
+ return this.request("POST", `/v1/machines`, {
230
+ body,
231
+ query: undefined,
232
+ init
233
+ });
238
234
  }
239
- async update(id, data) {
240
- return this.client._patch(`/api/tasks/${id}`, data);
235
+ async listPlans(query, init) {
236
+ return this.request("GET", `/v1/plans`, {
237
+ body: undefined,
238
+ query,
239
+ init
240
+ });
241
241
  }
242
- async delete(id) {
243
- return this.client._delete(`/api/tasks/${id}`);
242
+ async createPlan(body, init) {
243
+ return this.request("POST", `/v1/plans`, {
244
+ body,
245
+ query: undefined,
246
+ init
247
+ });
244
248
  }
245
- async start(id, agentId) {
246
- return this.client._post(`/api/tasks/${id}/start`, { agent_id: agentId });
249
+ async getPlan(id, init) {
250
+ return this.request("GET", `/v1/plans/${encodeURIComponent(String(id))}`, {
251
+ body: undefined,
252
+ query: undefined,
253
+ init
254
+ });
247
255
  }
248
- async complete(id, agentId) {
249
- return this.client._post(`/api/tasks/${id}/complete`, { agent_id: agentId });
256
+ async deletePlan(id, init) {
257
+ return this.request("DELETE", `/v1/plans/${encodeURIComponent(String(id))}`, {
258
+ body: undefined,
259
+ query: undefined,
260
+ init
261
+ });
250
262
  }
251
- async fail(id, options) {
252
- return this.client._post(`/api/tasks/${id}/fail`, options || {});
263
+ async updatePlan(id, body, init) {
264
+ return this.request("PATCH", `/v1/plans/${encodeURIComponent(String(id))}`, {
265
+ body,
266
+ query: undefined,
267
+ init
268
+ });
253
269
  }
254
- async logProgress(taskId, message, pctComplete, agentId) {
255
- return this.client._post(`/api/tasks/${taskId}/progress`, {
256
- message,
257
- pct_complete: pctComplete,
258
- agent_id: agentId
270
+ async listPlanComments(id, query, init) {
271
+ return this.request("GET", `/v1/plans/${encodeURIComponent(String(id))}/comments`, {
272
+ body: undefined,
273
+ query,
274
+ init
259
275
  });
260
276
  }
261
- async getProgress(id, options) {
262
- return this.client._get(`/api/tasks/${id}/progress`, buildQuery(options || {}));
277
+ async deletePlanPreserving(id, body, init) {
278
+ return this.request("POST", `/v1/plans/${encodeURIComponent(String(id))}/delete-preserving`, {
279
+ body,
280
+ query: undefined,
281
+ init
282
+ });
263
283
  }
264
- async getHistory(id, options) {
265
- return this.client._get(`/api/tasks/${id}/history`, buildQuery(options || {}));
284
+ async planPlanProjectLink(id, query, init) {
285
+ return this.request("GET", `/v1/plans/${encodeURIComponent(String(id))}/project-link`, {
286
+ body: undefined,
287
+ query,
288
+ init
289
+ });
266
290
  }
267
- async getAttachments(id) {
268
- return this.client._get(`/api/tasks/${id}/attachments`);
291
+ async applyPlanProjectLink(id, body, init) {
292
+ return this.request("POST", `/v1/plans/${encodeURIComponent(String(id))}/project-link`, {
293
+ body,
294
+ query: undefined,
295
+ init
296
+ });
269
297
  }
270
- async status(options) {
271
- return this.client._get("/api/tasks/status", buildQuery(options || {}));
298
+ async rollbackPlanProjectLink(id, body, init) {
299
+ return this.request("POST", `/v1/plans/${encodeURIComponent(String(id))}/project-link/rollback`, {
300
+ body,
301
+ query: undefined,
302
+ init
303
+ });
272
304
  }
273
- async next(options) {
274
- return this.client._get("/api/tasks/next", buildQuery(options || {}));
305
+ async admitPrGroup(body, init) {
306
+ return this.request("POST", `/v1/pr-groups/admit`, {
307
+ body,
308
+ query: undefined,
309
+ init
310
+ });
275
311
  }
276
- async active(projectId) {
277
- return this.client._get("/api/tasks/active", projectId ? `?project_id=${projectId}` : "");
312
+ async getPrGroupState(id, init) {
313
+ return this.request("GET", `/v1/pr-groups/${encodeURIComponent(String(id))}`, {
314
+ body: undefined,
315
+ query: undefined,
316
+ init
317
+ });
278
318
  }
279
- async stale(options) {
280
- return this.client._get("/api/tasks/stale", buildQuery(options || {}));
319
+ async getPrGroupEvents(id, query, init) {
320
+ return this.request("GET", `/v1/pr-groups/${encodeURIComponent(String(id))}/events`, {
321
+ body: undefined,
322
+ query,
323
+ init
324
+ });
281
325
  }
282
- async changedSince(since, projectId, options) {
283
- return this.client._get("/api/tasks/changed", buildQuery({ since, project_id: projectId, fields: options?.fields }));
326
+ async appendPrGroupEvent(id, body, init) {
327
+ return this.request("POST", `/v1/pr-groups/${encodeURIComponent(String(id))}/events`, {
328
+ body,
329
+ query: undefined,
330
+ init
331
+ });
284
332
  }
285
- async context(options) {
286
- const q = buildQuery({
287
- agent_id: options?.agentId,
288
- project_id: options?.projectId,
289
- format: options?.format,
290
- fields: options?.fields
333
+ async recoverPrGroup(id, body, init) {
334
+ return this.request("POST", `/v1/pr-groups/${encodeURIComponent(String(id))}/recover`, {
335
+ body,
336
+ query: undefined,
337
+ init
291
338
  });
292
- const url = `${this.client.baseUrl}/api/tasks/context${q}`;
293
- const res = await this.client._fetchRaw(url);
294
- if (!res.ok)
295
- return options?.format === "json" ? {} : "";
296
- if (options?.format === "json")
297
- return res.json();
298
- return res.text();
299
339
  }
300
- async export(options) {
301
- const q = buildQuery(options || {});
302
- const url = `${this.client.baseUrl}/api/tasks/export${q}`;
303
- const res = await this.client._fetchRaw(url);
304
- if (!res.ok)
305
- throw new TodosAPIError("Export failed", res.status, res.statusText, null);
306
- if (options?.format === "csv")
307
- return res.text();
308
- return res.json();
340
+ async getAtomicProjectMigrationCapability(init) {
341
+ return this.request("GET", `/v1/project-migrations`, {
342
+ body: undefined,
343
+ query: undefined,
344
+ init
345
+ });
309
346
  }
310
- async bulk(ids, action) {
311
- return this.client._post("/api/tasks/bulk", { ids, action });
347
+ async importAtomicProjectSnapshot(body, init) {
348
+ return this.request("POST", `/v1/project-migrations`, {
349
+ body,
350
+ query: undefined,
351
+ init
352
+ });
312
353
  }
313
- async claim(agentId, projectId) {
314
- return this.client._post("/api/tasks/claim", { agent_id: agentId, project_id: projectId });
354
+ async getProjectRegistrationCapability(init) {
355
+ return this.request("GET", `/v1/project-registration/capability`, {
356
+ body: undefined,
357
+ query: undefined,
358
+ init
359
+ });
315
360
  }
316
- async* subscribe(options = {}) {
317
- const q = buildQuery({
318
- agent_id: options.agentId,
319
- project_id: options.projectId,
320
- events: options.events?.join(",")
361
+ async compensateProjectRegistrationResource(body, init) {
362
+ return this.request("POST", `/v1/project-registration/compensate`, {
363
+ body,
364
+ query: undefined,
365
+ init
321
366
  });
322
- const url = `${this.client.baseUrl}/api/tasks/stream${q}`;
323
- const resp = await this.client._fetchRaw(url);
324
- if (!resp.ok || !resp.body)
325
- throw new Error(`SSE connection failed: ${resp.status}`);
326
- const reader = resp.body.getReader();
327
- const decoder = new TextDecoder;
328
- let buffer = "";
329
- try {
330
- while (true) {
331
- const { done, value } = await reader.read();
332
- if (done)
333
- break;
334
- buffer += decoder.decode(value, { stream: true });
335
- const lines = buffer.split(`
336
- `);
337
- buffer = lines.pop() || "";
338
- for (const line of lines) {
339
- if (line.startsWith("data: ")) {
340
- try {
341
- const data = JSON.parse(line.slice(6));
342
- if (data.type !== "connected")
343
- yield data;
344
- } catch {}
345
- }
346
- }
347
- }
348
- } finally {
349
- reader.releaseLock();
350
- }
351
367
  }
352
- }
353
-
354
- class AgentsResource {
355
- client;
356
- constructor(client) {
357
- this.client = client;
368
+ async createProjectRegistrationResource(body, init) {
369
+ return this.request("POST", `/v1/project-registration/create`, {
370
+ body,
371
+ query: undefined,
372
+ init
373
+ });
358
374
  }
359
- async list(options) {
360
- return this.client._get("/api/agents", buildQuery(options || {}));
375
+ async readExactProjectRegistrationResource(body, init) {
376
+ return this.request("POST", `/v1/project-registration/read-exact`, {
377
+ body,
378
+ query: undefined,
379
+ init
380
+ });
361
381
  }
362
- async register(data) {
363
- return this.client._post("/api/agents", data);
382
+ async lookupProjectRegistrationReceipt(body, init) {
383
+ return this.request("POST", `/v1/project-registration/receipts/lookup`, {
384
+ body,
385
+ query: undefined,
386
+ init
387
+ });
364
388
  }
365
- async fullRegister(data) {
366
- return this.client._post("/api/agents", data);
389
+ async listProjectRegistrationResources(query, init) {
390
+ return this.request("GET", `/v1/project-registration/resources`, {
391
+ body: undefined,
392
+ query,
393
+ init
394
+ });
367
395
  }
368
- async update(id, data) {
369
- return this.client._patch(`/api/agents/${id}`, data);
396
+ async validatePriorRegistrationAdoption(body, init) {
397
+ const response = await this.request("POST", `/v1/project-registration/validate-prior-adoption`, {
398
+ body,
399
+ query: undefined,
400
+ init
401
+ });
402
+ return {
403
+ validation: assertTodosPriorRegistrationAdoptionValidationEnvelope(response, body)
404
+ };
370
405
  }
371
- async delete(id) {
372
- return this.client._delete(`/api/agents/${id}`);
406
+ async verifyInverseProjectRegistrationResource(body, init) {
407
+ return this.request("POST", `/v1/project-registration/verify-inverse`, {
408
+ body,
409
+ query: undefined,
410
+ init
411
+ });
373
412
  }
374
- async bulkDelete(ids) {
375
- return this.client._post("/api/agents/bulk", { ids, action: "delete" });
413
+ async listProjects(init) {
414
+ return this.request("GET", `/v1/projects`, {
415
+ body: undefined,
416
+ query: undefined,
417
+ init
418
+ });
376
419
  }
377
- async me(name) {
378
- return this.client._get("/api/agents/me", `?name=${encodeURIComponent(name)}`);
420
+ async createProject(body, init) {
421
+ return this.request("POST", `/v1/projects`, {
422
+ body,
423
+ query: undefined,
424
+ init
425
+ });
379
426
  }
380
- async queue(agentId) {
381
- return this.client._get(`/api/agents/${encodeURIComponent(agentId)}/queue`);
427
+ async getProject(id, init) {
428
+ return this.request("GET", `/v1/projects/${encodeURIComponent(String(id))}`, {
429
+ body: undefined,
430
+ query: undefined,
431
+ init
432
+ });
382
433
  }
383
- async team(agentId) {
384
- return this.client._get(`/api/agents/${encodeURIComponent(agentId)}/team`);
434
+ async deleteProject(id, init) {
435
+ return this.request("DELETE", `/v1/projects/${encodeURIComponent(String(id))}`, {
436
+ body: undefined,
437
+ query: undefined,
438
+ init
439
+ });
385
440
  }
386
- async orgChart() {
387
- return this.client._get("/api/org");
441
+ async updateProject(id, body, init) {
442
+ return this.request("PATCH", `/v1/projects/${encodeURIComponent(String(id))}`, {
443
+ body,
444
+ query: undefined,
445
+ init
446
+ });
388
447
  }
389
- }
390
-
391
- class ProjectsResource {
392
- client;
393
- constructor(client) {
394
- this.client = client;
448
+ async deleteProjectPreserving(id, body, init) {
449
+ return this.request("POST", `/v1/projects/${encodeURIComponent(String(id))}/delete-preserving`, {
450
+ body,
451
+ query: undefined,
452
+ init
453
+ });
395
454
  }
396
- async list(options) {
397
- return this.client._get("/api/projects", buildQuery(options || {}));
455
+ async renameProject(id, body, init) {
456
+ return this.request("POST", `/v1/projects/${encodeURIComponent(String(id))}/rename`, {
457
+ body,
458
+ query: undefined,
459
+ init
460
+ });
398
461
  }
399
- async create(data) {
400
- return this.client._post("/api/projects", data);
462
+ async planProjectTaskListEnsure(id, init) {
463
+ return this.request("GET", `/v1/projects/${encodeURIComponent(String(id))}/task-list/ensure`, {
464
+ body: undefined,
465
+ query: undefined,
466
+ init
467
+ });
401
468
  }
402
- async delete(id) {
403
- return this.client._delete(`/api/projects/${id}`);
469
+ async ensureProjectTaskList(id, body, init) {
470
+ return this.request("POST", `/v1/projects/${encodeURIComponent(String(id))}/task-list/ensure`, {
471
+ body,
472
+ query: undefined,
473
+ init
474
+ });
404
475
  }
405
- async bulkDelete(ids) {
406
- return this.client._post("/api/projects/bulk", { ids, action: "delete" });
476
+ async rollbackProjectTaskListEnsure(id, body, init) {
477
+ return this.request("POST", `/v1/projects/${encodeURIComponent(String(id))}/task-list/rollback`, {
478
+ body,
479
+ query: undefined,
480
+ init
481
+ });
407
482
  }
408
- }
409
-
410
- class PlansResource {
411
- client;
412
- constructor(client) {
413
- this.client = client;
414
- }
415
- async list(projectId) {
416
- return this.client._get("/api/plans", projectId ? `?project_id=${projectId}` : "");
483
+ async findTaskGitRefs(ref, init) {
484
+ return this.request("GET", `/v1/refs/${encodeURIComponent(String(ref))}`, {
485
+ body: undefined,
486
+ query: undefined,
487
+ init
488
+ });
417
489
  }
418
- async create(data) {
419
- return this.client._post("/api/plans", data);
490
+ async getStats(init) {
491
+ return this.request("GET", `/v1/stats`, {
492
+ body: undefined,
493
+ query: undefined,
494
+ init
495
+ });
420
496
  }
421
- async get(id) {
422
- return this.client._get(`/api/plans/${id}`);
497
+ async listTaskLists(query, init) {
498
+ return this.request("GET", `/v1/task-lists`, {
499
+ body: undefined,
500
+ query,
501
+ init
502
+ });
423
503
  }
424
- async update(id, data) {
425
- return this.client._patch(`/api/plans/${id}`, data);
504
+ async createTaskList(body, init) {
505
+ return this.request("POST", `/v1/task-lists`, {
506
+ body,
507
+ query: undefined,
508
+ init
509
+ });
426
510
  }
427
- async delete(id) {
428
- return this.client._delete(`/api/plans/${id}`);
511
+ async getTaskList(id, init) {
512
+ return this.request("GET", `/v1/task-lists/${encodeURIComponent(String(id))}`, {
513
+ body: undefined,
514
+ query: undefined,
515
+ init
516
+ });
429
517
  }
430
- async bulkDelete(ids) {
431
- return this.client._post("/api/plans/bulk", { ids, action: "delete" });
518
+ async deleteTaskList(id, init) {
519
+ return this.request("DELETE", `/v1/task-lists/${encodeURIComponent(String(id))}`, {
520
+ body: undefined,
521
+ query: undefined,
522
+ init
523
+ });
432
524
  }
433
- }
434
-
435
- class OrgsResource {
436
- client;
437
- constructor(client) {
438
- this.client = client;
525
+ async updateTaskList(id, body, init) {
526
+ return this.request("PATCH", `/v1/task-lists/${encodeURIComponent(String(id))}`, {
527
+ body,
528
+ query: undefined,
529
+ init
530
+ });
439
531
  }
440
- async list() {
441
- return this.client._get("/api/orgs");
532
+ async deleteTaskListPreserving(id, body, init) {
533
+ return this.request("POST", `/v1/task-lists/${encodeURIComponent(String(id))}/delete-preserving`, {
534
+ body,
535
+ query: undefined,
536
+ init
537
+ });
442
538
  }
443
- async create(data) {
444
- return this.client._post("/api/orgs", data);
539
+ async applyTaskManifest(body, init) {
540
+ return this.request("POST", `/v1/task-manifest/apply`, {
541
+ body,
542
+ query: undefined,
543
+ init
544
+ });
445
545
  }
446
- async update(id, data) {
447
- return this.client._patch(`/api/orgs/${id}`, data);
546
+ async lookupTaskManifestBinding(body, init) {
547
+ return this.request("POST", `/v1/task-manifest/bindings/lookup`, {
548
+ body,
549
+ query: undefined,
550
+ init
551
+ });
448
552
  }
449
- async delete(id) {
450
- return this.client._delete(`/api/orgs/${id}`);
553
+ async getTaskManifestCapability(init) {
554
+ return this.request("GET", `/v1/task-manifest/capability`, {
555
+ body: undefined,
556
+ query: undefined,
557
+ init
558
+ });
451
559
  }
452
- }
453
-
454
- class WebhooksResource {
455
- client;
456
- constructor(client) {
457
- this.client = client;
560
+ async compensateTaskManifest(body, init) {
561
+ return this.request("POST", `/v1/task-manifest/compensate`, {
562
+ body,
563
+ query: undefined,
564
+ init
565
+ });
458
566
  }
459
- async list() {
460
- return this.client._get("/api/webhooks");
567
+ async readExactTaskManifest(body, init) {
568
+ return this.request("POST", `/v1/task-manifest/read-exact`, {
569
+ body,
570
+ query: undefined,
571
+ init
572
+ });
461
573
  }
462
- async create(data) {
463
- return this.client._post("/api/webhooks", data);
574
+ async applyTaskSubtreeTransfer(body, init) {
575
+ return this.request("POST", `/v1/task-subtree-transfer/apply`, {
576
+ body,
577
+ query: undefined,
578
+ init
579
+ });
464
580
  }
465
- async delete(id) {
466
- return this.client._delete(`/api/webhooks/${id}`);
581
+ async getTaskSubtreeTransferCapability(init) {
582
+ return this.request("GET", `/v1/task-subtree-transfer/capability`, {
583
+ body: undefined,
584
+ query: undefined,
585
+ init
586
+ });
467
587
  }
468
- }
469
-
470
- class TemplatesResource {
471
- client;
472
- constructor(client) {
473
- this.client = client;
588
+ async inspectTaskSubtreeTransfer(body, init) {
589
+ return this.request("POST", `/v1/task-subtree-transfer/inspect`, {
590
+ body,
591
+ query: undefined,
592
+ init
593
+ });
474
594
  }
475
- async list() {
476
- return this.client._get("/api/templates");
595
+ async readExactTaskSubtreeTransfer(body, init) {
596
+ return this.request("POST", `/v1/task-subtree-transfer/read-exact`, {
597
+ body,
598
+ query: undefined,
599
+ init
600
+ });
477
601
  }
478
- async create(data) {
479
- return this.client._post("/api/templates", data);
602
+ async rollbackTaskSubtreeTransfer(body, init) {
603
+ return this.request("POST", `/v1/task-subtree-transfer/rollback`, {
604
+ body,
605
+ query: undefined,
606
+ init
607
+ });
480
608
  }
481
- async delete(id) {
482
- return this.client._delete(`/api/templates/${id}`);
609
+ async listTasks(query, init) {
610
+ return this.request("GET", `/v1/tasks`, {
611
+ body: undefined,
612
+ query,
613
+ init
614
+ });
483
615
  }
484
- }
485
-
486
- class PrGroupsResource {
487
- client;
488
- constructor(client) {
489
- this.client = client;
616
+ async createTask(body, init) {
617
+ return this.request("POST", `/v1/tasks`, {
618
+ body,
619
+ query: undefined,
620
+ init
621
+ });
490
622
  }
491
- async admit(input) {
492
- return this.client._post("/api/pr-groups/admit", input);
623
+ async getTask(id, init) {
624
+ return this.request("GET", `/v1/tasks/${encodeURIComponent(String(id))}`, {
625
+ body: undefined,
626
+ query: undefined,
627
+ init
628
+ });
493
629
  }
494
- async get(groupId) {
495
- const result = await this.client._get(`/api/pr-groups/${encodeURIComponent(groupId)}`);
496
- return result.view;
630
+ async deleteTask(id, init) {
631
+ return this.request("DELETE", `/v1/tasks/${encodeURIComponent(String(id))}`, {
632
+ body: undefined,
633
+ query: undefined,
634
+ init
635
+ });
497
636
  }
498
- async events(groupId, options = {}) {
499
- const result = await this.client._get(`/api/pr-groups/${encodeURIComponent(groupId)}/events`, buildQuery({
500
- limit: options.limit,
501
- after_sequence: options.after_sequence
502
- }));
503
- return result.history;
637
+ async updateTask(id, body, init) {
638
+ return this.request("PATCH", `/v1/tasks/${encodeURIComponent(String(id))}`, {
639
+ body,
640
+ query: undefined,
641
+ init
642
+ });
504
643
  }
505
- async append(input) {
506
- return this.client._post(`/api/pr-groups/${encodeURIComponent(input.group_id)}/events`, input);
644
+ async listTaskComments(id, query, init) {
645
+ return this.request("GET", `/v1/tasks/${encodeURIComponent(String(id))}/comments`, {
646
+ body: undefined,
647
+ query,
648
+ init
649
+ });
507
650
  }
508
- async recover(input) {
509
- return this.client._post(`/api/pr-groups/${encodeURIComponent(input.group_id)}/recover`, input);
651
+ async createTaskComment(id, body, init) {
652
+ return this.request("POST", `/v1/tasks/${encodeURIComponent(String(id))}/comments`, {
653
+ body,
654
+ query: undefined,
655
+ init
656
+ });
510
657
  }
511
- }
512
-
513
- class TodosClient {
514
- baseUrl;
515
- timeout;
516
- apiKey;
517
- maxRetries;
518
- retryDelay;
519
- tasks;
520
- agents;
521
- projects;
522
- plans;
523
- orgs;
524
- webhooks;
525
- templates;
526
- prGroups;
527
- constructor(options = {}) {
528
- const localConfig = getLocalApiConfig();
529
- this.baseUrl = normalizeApiUrl(options.baseUrl) || localConfig.apiUrl || "http://localhost:19427";
530
- this.timeout = options.timeout ?? 1e4;
531
- this.apiKey = options.apiKey || localConfig.apiKey;
532
- this.maxRetries = options.maxRetries ?? 0;
533
- this.retryDelay = options.retryDelay ?? 1000;
534
- this.tasks = new TasksResource(this);
535
- this.agents = new AgentsResource(this);
536
- this.projects = new ProjectsResource(this);
537
- this.plans = new PlansResource(this);
538
- this.orgs = new OrgsResource(this);
539
- this.webhooks = new WebhooksResource(this);
540
- this.templates = new TemplatesResource(this);
541
- this.prGroups = new PrGroupsResource(this);
658
+ async completeTask(id, body, init) {
659
+ return this.request("POST", `/v1/tasks/${encodeURIComponent(String(id))}/complete`, {
660
+ body,
661
+ query: undefined,
662
+ init
663
+ });
542
664
  }
543
- static fromEnv(apiKey) {
544
- return new TodosClient({ apiKey });
665
+ async failTask(id, body, init) {
666
+ return this.request("POST", `/v1/tasks/${encodeURIComponent(String(id))}/fail`, {
667
+ body,
668
+ query: undefined,
669
+ init
670
+ });
545
671
  }
546
- async _fetchRaw(url, init) {
547
- const controller = new AbortController;
548
- const timer = setTimeout(() => controller.abort(), this.timeout);
549
- try {
550
- const headers = this._buildHeaders(init?.headers);
551
- return await fetch(url, { ...init, headers, signal: controller.signal });
552
- } finally {
553
- clearTimeout(timer);
554
- }
672
+ async listTaskGitRefs(id, init) {
673
+ return this.request("GET", `/v1/tasks/${encodeURIComponent(String(id))}/refs`, {
674
+ body: undefined,
675
+ query: undefined,
676
+ init
677
+ });
555
678
  }
556
- _buildHeaders(existing) {
557
- const headers = { "Content-Type": "application/json" };
558
- if (this.apiKey)
559
- headers["x-api-key"] = this.apiKey;
560
- if (existing) {
561
- if (existing instanceof Headers) {
562
- existing.forEach((v, k) => {
563
- headers[k] = v;
564
- });
565
- } else if (Array.isArray(existing)) {
566
- for (const [k, v] of existing)
567
- headers[k] = v;
568
- } else {
569
- Object.assign(headers, existing);
570
- }
571
- }
572
- return headers;
679
+ async linkTaskGitRef(id, body, init) {
680
+ return this.request("POST", `/v1/tasks/${encodeURIComponent(String(id))}/refs`, {
681
+ body,
682
+ query: undefined,
683
+ init
684
+ });
573
685
  }
574
- async _fetchWithRetry(path, init) {
575
- let lastError = null;
576
- const maxAttempts = this.maxRetries + 1;
577
- for (let attempt = 0;attempt < maxAttempts; attempt++) {
578
- try {
579
- return await this._fetch(path, init);
580
- } catch (e) {
581
- lastError = e;
582
- if (e instanceof TodosAPIError && e.status < 500 && e.status !== 429)
583
- throw e;
584
- if (e instanceof TodosUnauthorizedError || e instanceof TodosNotFoundError || e instanceof TodosConflictError)
585
- throw e;
586
- if (attempt < maxAttempts - 1) {
587
- const delay = this.retryDelay * Math.pow(2, attempt);
588
- if (e instanceof TodosRateLimitError) {
589
- await this._sleep(e.retryAfter * 1000);
590
- } else {
591
- await this._sleep(delay);
592
- }
593
- }
594
- }
595
- }
596
- throw lastError || new Error("Request failed after retries");
686
+ async handoffStaleTaskLock(id, body, init) {
687
+ return this.request("POST", `/v1/tasks/${encodeURIComponent(String(id))}/stale-lock-handoff`, {
688
+ body,
689
+ query: undefined,
690
+ init
691
+ });
597
692
  }
598
- async _fetch(path, init) {
599
- const controller = new AbortController;
600
- const timer = setTimeout(() => controller.abort(), this.timeout);
601
- try {
602
- const url = `${this.baseUrl}${path}`;
603
- const headers = this._buildHeaders(init?.headers);
604
- const res = await fetch(url, { ...init, headers, signal: controller.signal });
605
- if (!res.ok) {
606
- const body = await res.json().catch(() => ({ error: res.statusText }));
607
- const message = body.error || `HTTP ${res.status}: ${res.statusText}`;
608
- if (res.status === 401)
609
- throw new TodosUnauthorizedError(message, body);
610
- if (res.status === 404)
611
- throw new TodosNotFoundError(message, body);
612
- if (res.status === 409)
613
- throw new TodosConflictError(message, body);
614
- if (res.status === 429) {
615
- const retryAfter = parseInt(res.headers.get("retry-after") || "60", 10);
616
- throw new TodosRateLimitError(message, retryAfter, body);
617
- }
618
- throw new TodosAPIError(message, res.status, res.statusText, body);
619
- }
620
- if (res.status === 204)
621
- return null;
622
- const text = await res.text();
623
- if (text.length === 0)
624
- return null;
625
- return JSON.parse(text);
626
- } catch (e) {
627
- if (e instanceof DOMException && e.name === "AbortError") {
628
- throw new TodosTimeoutError(this.timeout);
629
- }
630
- throw e;
631
- } finally {
632
- clearTimeout(timer);
633
- }
693
+ async startTask(id, init) {
694
+ return this.request("POST", `/v1/tasks/${encodeURIComponent(String(id))}/start`, {
695
+ body: undefined,
696
+ query: undefined,
697
+ init
698
+ });
634
699
  }
635
- async _get(path, query = "") {
636
- return this._fetchWithRetry(`${path}${query}`);
700
+ async listTemplates(query, init) {
701
+ return this.request("GET", `/v1/templates`, {
702
+ body: undefined,
703
+ query,
704
+ init
705
+ });
637
706
  }
638
- async _post(path, body) {
639
- return this._fetchWithRetry(path, {
640
- method: "POST",
641
- body: body ? JSON.stringify(body) : undefined
707
+ async createTemplate(body, init) {
708
+ return this.request("POST", `/v1/templates`, {
709
+ body,
710
+ query: undefined,
711
+ init
642
712
  });
643
713
  }
644
- async _patch(path, body) {
645
- return this._fetchWithRetry(path, {
646
- method: "PATCH",
647
- body: JSON.stringify(body)
714
+ async initializeTemplates(body, init) {
715
+ return this.request("POST", `/v1/templates/initialize`, {
716
+ body,
717
+ query: undefined,
718
+ init
648
719
  });
649
720
  }
650
- async _delete(path) {
651
- return this._fetchWithRetry(path, { method: "DELETE" });
721
+ async getTemplate(id, init) {
722
+ return this.request("GET", `/v1/templates/${encodeURIComponent(String(id))}`, {
723
+ body: undefined,
724
+ query: undefined,
725
+ init
726
+ });
652
727
  }
653
- _sleep(ms) {
654
- return new Promise((resolve2) => setTimeout(resolve2, ms));
728
+ async deleteTemplate(id, init) {
729
+ return this.request("DELETE", `/v1/templates/${encodeURIComponent(String(id))}`, {
730
+ body: undefined,
731
+ query: undefined,
732
+ init
733
+ });
655
734
  }
656
- async getHealth() {
657
- return this._get("/api/health");
735
+ async updateTemplate(id, body, init) {
736
+ return this.request("PATCH", `/v1/templates/${encodeURIComponent(String(id))}`, {
737
+ body,
738
+ query: undefined,
739
+ init
740
+ });
658
741
  }
659
- async isAlive() {
660
- try {
661
- await this._get("/api/stats");
662
- return true;
663
- } catch {
664
- return false;
665
- }
742
+ async getTemplateHistory(id, init) {
743
+ return this.request("GET", `/v1/templates/${encodeURIComponent(String(id))}/history`, {
744
+ body: undefined,
745
+ query: undefined,
746
+ init
747
+ });
666
748
  }
667
- async getStats() {
668
- return this._get("/api/stats");
749
+ }
750
+
751
+ // src/lib/local-opt-in.ts
752
+ import {
753
+ clientTransportEnvKeys,
754
+ credentialOverrideEnvKey,
755
+ credentialPointerEnvKey,
756
+ CREDENTIAL_PROFILE_ENV_KEY
757
+ } from "@hasna/contracts/client";
758
+ var TODOS_LOCAL_OPT_IN_ENV_KEYS = ["HASNA_TODOS_LOCAL", "TODOS_LOCAL"];
759
+ function isTodosLocalOptIn(env = process.env) {
760
+ return TODOS_LOCAL_OPT_IN_ENV_KEYS.some((key) => (env[key] ?? "").trim() !== "");
761
+ }
762
+ function todosAuthorityEnvKeys() {
763
+ const keys = clientTransportEnvKeys("todos");
764
+ return [
765
+ ...keys.apiUrlKeys,
766
+ ...keys.apiKeyKeys,
767
+ credentialOverrideEnvKey("todos"),
768
+ credentialPointerEnvKey("todos"),
769
+ CREDENTIAL_PROFILE_ENV_KEY
770
+ ];
771
+ }
772
+ function hasTodosEnvAuthorityIntent(env = process.env) {
773
+ return todosAuthorityEnvKeys().some((key) => (env[key] ?? "").trim() !== "");
774
+ }
775
+ function selectsTodosLocalStore(env = process.env) {
776
+ return !hasTodosEnvAuthorityIntent(env) && isTodosLocalOptIn(env);
777
+ }
778
+ function todosResolverEnv(env) {
779
+ const blanks = todosAuthorityEnvKeys().filter((key) => (key in env) && (env[key] ?? "").trim() === "");
780
+ if (blanks.length === 0)
781
+ return env;
782
+ const next = { ...env };
783
+ for (const key of blanks)
784
+ delete next[key];
785
+ return next;
786
+ }
787
+ var CONTRACTS_AMBIENT_ENVIRONMENT = Symbol.for("hasna:contracts:ambientClientEnvironment");
788
+ function isAmbientTodosEnv(env) {
789
+ if (typeof process !== "undefined" && env === process.env)
790
+ return true;
791
+ return env[CONTRACTS_AMBIENT_ENVIRONMENT] === true;
792
+ }
793
+ function todosResolverInputs(env, credentials = {}) {
794
+ const normalised = todosResolverEnv(env);
795
+ if (normalised === env)
796
+ return { env: normalised, credentials };
797
+ const keychain = { ...credentials.keychain };
798
+ if (keychain.enabled === undefined && keychain.run === undefined) {
799
+ keychain.enabled = isAmbientTodosEnv(env);
800
+ }
801
+ return { env: normalised, credentials: { ...credentials, keychain } };
802
+ }
803
+
804
+ // src/sdk/resolve.ts
805
+ var TODOS_LOCAL_SERVE_URL = "http://localhost:19427";
806
+ var localNoticePrinted = false;
807
+ function __resetTodosSdkLocalNotice() {
808
+ localNoticePrinted = false;
809
+ }
810
+ function stripV1(baseUrl) {
811
+ return baseUrl.replace(/\/+$/, "").replace(/\/v1$/, "");
812
+ }
813
+ function announceLocal(notice, reason) {
814
+ if (localNoticePrinted)
815
+ return;
816
+ localNoticePrinted = true;
817
+ const line = `todos: LOCAL mode \u2014 no Hasna credential resolved (${reason}); reading and writing the local ` + `todos-serve at ${TODOS_LOCAL_SERVE_URL}, not the hosted fleet. Set HASNA_TODOS_API_KEY, add the ` + `Keychain item hasna.credentials.todos.api-key, or write ~/.hasna/todos/config/credentials to go hosted.`;
818
+ if (notice)
819
+ notice(line);
820
+ else if (typeof process !== "undefined")
821
+ process.stderr.write(`${line}
822
+ `);
823
+ }
824
+ function resolveTodosSdkTransport(options = {}) {
825
+ const rawEnv = options.env ?? (typeof process !== "undefined" ? process.env : {});
826
+ const requestedCredentials = {
827
+ ...options.credentials,
828
+ ...options.apiKey !== undefined ? { apiKey: options.apiKey } : {}
829
+ };
830
+ const { env, credentials } = todosResolverInputs(rawEnv, requestedCredentials);
831
+ if (options.baseUrl) {
832
+ return {
833
+ mode: "http",
834
+ baseUrl: stripV1(options.baseUrl),
835
+ apiKey: options.apiKey ?? null,
836
+ apiKeySource: options.apiKey ? "explicit apiKey argument" : null,
837
+ apiUrlSource: "explicit baseUrl argument"
838
+ };
669
839
  }
670
- async getReport(options) {
671
- return this._get("/api/report", buildQuery({ days: options?.days, project_id: options?.projectId }));
840
+ if (selectsTodosLocalStore(env)) {
841
+ announceLocal(options.notice, "HASNA_TODOS_LOCAL is set and nothing configures an authority");
842
+ return {
843
+ mode: "local-serve",
844
+ baseUrl: TODOS_LOCAL_SERVE_URL,
845
+ apiKey: options.apiKey ?? null,
846
+ apiKeySource: options.apiKey ? "explicit apiKey argument" : null,
847
+ apiUrlSource: "local-serve"
848
+ };
672
849
  }
673
- async doctor() {
674
- return this._get("/api/doctor");
850
+ const credential = resolveCredential("todos", env, credentials);
851
+ const chainOptions = {
852
+ credentials: credential ? { ...credentials, apiKey: credential.apiKey } : credentials
853
+ };
854
+ let resolution;
855
+ try {
856
+ resolution = resolveClientTransport("todos", env, chainOptions);
857
+ } catch (error) {
858
+ if (error instanceof ClientTransportConfigurationError && /is not set and no API key could be resolved/.test(error.message)) {
859
+ announceLocal(options.notice, "nothing configured a Hasna Todos credential");
860
+ return {
861
+ mode: "local-serve",
862
+ baseUrl: TODOS_LOCAL_SERVE_URL,
863
+ apiKey: null,
864
+ apiKeySource: null,
865
+ apiUrlSource: "local-serve"
866
+ };
867
+ }
868
+ throw error;
675
869
  }
676
- async activity(limit) {
677
- return this._get("/api/activity", limit ? `?limit=${limit}` : "");
870
+ return {
871
+ mode: "http",
872
+ baseUrl: stripV1(resolution.baseUrl),
873
+ apiKey: credential ? credential.apiKey : null,
874
+ apiKeySource: credential ? credential.source : resolution.apiKeySource,
875
+ apiUrlSource: resolution.apiUrlSource ?? "default"
876
+ };
877
+ }
878
+ function createTodosV1Client(options = {}) {
879
+ const resolved = resolveTodosSdkTransport(options);
880
+ if (resolved.mode !== "http" || !resolved.apiKey) {
881
+ throw new Error("TODOS_CREDENTIAL_MISSING: the /v1 client is hosted-only and no Hasna Todos credential resolved. " + "Looked at HASNA_TODOS_API_KEY_OVERRIDE / HASNA_PROFILE / HASNA_TODOS_API_KEY_REF, the Keychain item " + "hasna.credentials.todos.api-key, ~/.hasna/todos/config/credentials, then HASNA_TODOS_API_KEY.");
882
+ }
883
+ const baseFetch = options.fetch ?? ((input, init) => fetch(input, init));
884
+ const refreshOptions = { ...options, notice: () => {} };
885
+ const fetchWithFreshCredential = (input, init) => {
886
+ const headers = {};
887
+ new Headers(init?.headers ?? {}).forEach((value, key) => {
888
+ headers[key] = value;
889
+ });
890
+ try {
891
+ const fresh = resolveTodosSdkTransport(refreshOptions).apiKey;
892
+ if (fresh)
893
+ headers["x-api-key"] = fresh;
894
+ } catch {}
895
+ return baseFetch(input, { ...init, headers });
896
+ };
897
+ return new TodosV1Client({
898
+ baseUrl: resolved.baseUrl,
899
+ apiKey: resolved.apiKey,
900
+ fetch: fetchWithFreshCredential,
901
+ ...options.headers ? { headers: options.headers } : {}
902
+ });
903
+ }
904
+
905
+ // src/sdk/client.ts
906
+ function buildQuery(params) {
907
+ const search = new URLSearchParams;
908
+ for (const [k, v] of Object.entries(params)) {
909
+ if (v !== undefined)
910
+ search.set(k, Array.isArray(v) ? v.join(",") : String(v));
678
911
  }
679
- async listTasks(filter = {}) {
680
- return this.tasks.list(filter);
912
+ const s = search.toString();
913
+ return s ? `?${s}` : "";
914
+ }
915
+
916
+ class TasksResource {
917
+ client;
918
+ constructor(client) {
919
+ this.client = client;
681
920
  }
682
- async getTask(id, options) {
683
- return this.tasks.get(id, options);
921
+ async list(options) {
922
+ return this.client._get("/api/tasks", buildQuery(options || {}));
684
923
  }
685
- async createTask(data) {
686
- return this.tasks.create(data);
924
+ async get(id, options) {
925
+ return this.client._get(`/api/tasks/${id}`, buildQuery(options || {}));
687
926
  }
688
- async updateTask(id, data) {
689
- return this.tasks.update(id, data);
927
+ async getWithRelations(id) {
928
+ return this.client._get(`/api/tasks/${id}`, buildQuery({ format: "full" }));
690
929
  }
691
- async deleteTask(id) {
692
- await this.tasks.delete(id);
930
+ async create(data) {
931
+ return this.client._post("/api/tasks", data);
693
932
  }
694
- async startTask(id, agentId) {
695
- return this.tasks.start(id, agentId);
933
+ async update(id, data) {
934
+ return this.client._patch(`/api/tasks/${id}`, data);
696
935
  }
697
- async completeTask(id, agentId) {
698
- return this.tasks.complete(id, agentId);
936
+ async delete(id) {
937
+ return this.client._delete(`/api/tasks/${id}`);
699
938
  }
700
- async failTask(id, options = {}) {
701
- return this.tasks.fail(id, options);
939
+ async start(id, agentId) {
940
+ return this.client._post(`/api/tasks/${id}/start`, { agent_id: agentId });
941
+ }
942
+ async complete(id, agentId) {
943
+ return this.client._post(`/api/tasks/${id}/complete`, { agent_id: agentId });
944
+ }
945
+ async fail(id, options) {
946
+ return this.client._post(`/api/tasks/${id}/fail`, options || {});
702
947
  }
703
948
  async logProgress(taskId, message, pctComplete, agentId) {
704
- return this.tasks.logProgress(taskId, message, pctComplete, agentId);
949
+ return this.client._post(`/api/tasks/${taskId}/progress`, {
950
+ message,
951
+ pct_complete: pctComplete,
952
+ agent_id: agentId
953
+ });
705
954
  }
706
- async getStatus(projectId, agentId) {
707
- return this.tasks.status({ project_id: projectId, agent_id: agentId });
955
+ async getProgress(id, options) {
956
+ return this.client._get(`/api/tasks/${id}/progress`, buildQuery(options || {}));
708
957
  }
709
- async getActiveWork(projectId) {
710
- const res = await this.tasks.active(projectId);
711
- return res.active;
958
+ async getHistory(id, options) {
959
+ return this.client._get(`/api/tasks/${id}/history`, buildQuery(options || {}));
712
960
  }
713
- async getTasksChangedSince(since, projectId) {
714
- return this.tasks.changedSince(since, projectId);
961
+ async getAttachments(id) {
962
+ return this.client._get(`/api/tasks/${id}/attachments`);
715
963
  }
716
- async getStaleTasks(minutes, projectId) {
717
- return this.tasks.stale({ minutes, project_id: projectId });
964
+ async status(options) {
965
+ return this.client._get("/api/tasks/status", buildQuery(options || {}));
718
966
  }
719
- async getContext(options = {}) {
720
- return this.tasks.context(options);
967
+ async next(options) {
968
+ return this.client._get("/api/tasks/next", buildQuery(options || {}));
721
969
  }
722
- async exportTasks(filter = {}) {
723
- return this.tasks.export(filter);
970
+ async active(projectId) {
971
+ return this.client._get("/api/tasks/active", projectId ? `?project_id=${projectId}` : "");
724
972
  }
725
- async claimNextTask(agentId, projectId) {
726
- return this.tasks.claim(agentId, projectId);
973
+ async stale(options) {
974
+ return this.client._get("/api/tasks/stale", buildQuery(options || {}));
727
975
  }
728
- async getTaskHistory(id, options) {
729
- return this.tasks.getHistory(id, options);
976
+ async changedSince(since, projectId, options) {
977
+ return this.client._get("/api/tasks/changed", buildQuery({ since, project_id: projectId, fields: options?.fields }));
730
978
  }
731
- async getTaskAttachments(id) {
732
- return this.tasks.getAttachments(id);
979
+ async context(options) {
980
+ const q = buildQuery({
981
+ agent_id: options?.agentId,
982
+ project_id: options?.projectId,
983
+ format: options?.format,
984
+ fields: options?.fields
985
+ });
986
+ const url = `${this.client.baseUrl}/api/tasks/context${q}`;
987
+ const res = await this.client._fetchRaw(url);
988
+ if (!res.ok)
989
+ return options?.format === "json" ? {} : "";
990
+ if (options?.format === "json")
991
+ return res.json();
992
+ return res.text();
733
993
  }
734
- async getTaskProgress(id, options) {
735
- return this.tasks.getProgress(id, options);
994
+ async export(options) {
995
+ const q = buildQuery(options || {});
996
+ const url = `${this.client.baseUrl}/api/tasks/export${q}`;
997
+ const res = await this.client._fetchRaw(url);
998
+ if (!res.ok)
999
+ throw new TodosAPIError("Export failed", res.status, res.statusText, null);
1000
+ if (options?.format === "csv")
1001
+ return res.text();
1002
+ return res.json();
736
1003
  }
737
- async* subscribeToStream(options = {}) {
738
- yield* this.tasks.subscribe(options);
1004
+ async bulk(ids, action) {
1005
+ return this.client._post("/api/tasks/bulk", { ids, action });
739
1006
  }
740
- async getProjects() {
741
- return this.projects.list();
1007
+ async claim(agentId, projectId) {
1008
+ return this.client._post("/api/tasks/claim", { agent_id: agentId, project_id: projectId });
742
1009
  }
743
- }
744
- function createClient(options) {
745
- return new TodosClient(options);
746
- }
747
- // src/project-registration/types.ts
748
- class TodosProjectRegistrationError extends Error {
749
- code;
750
- details;
751
- constructor(code, message, details = {}) {
752
- super(message);
753
- this.code = code;
754
- this.details = details;
755
- this.name = "TodosProjectRegistrationError";
1010
+ async* subscribe(options = {}) {
1011
+ const q = buildQuery({
1012
+ agent_id: options.agentId,
1013
+ project_id: options.projectId,
1014
+ events: options.events?.join(",")
1015
+ });
1016
+ const url = `${this.client.baseUrl}/api/tasks/stream${q}`;
1017
+ const resp = await this.client._fetchRaw(url);
1018
+ if (!resp.ok || !resp.body)
1019
+ throw new Error(`SSE connection failed: ${resp.status}`);
1020
+ const reader = resp.body.getReader();
1021
+ const decoder = new TextDecoder;
1022
+ let buffer = "";
1023
+ try {
1024
+ while (true) {
1025
+ const { done, value } = await reader.read();
1026
+ if (done)
1027
+ break;
1028
+ buffer += decoder.decode(value, { stream: true });
1029
+ const lines = buffer.split(`
1030
+ `);
1031
+ buffer = lines.pop() || "";
1032
+ for (const line of lines) {
1033
+ if (line.startsWith("data: ")) {
1034
+ try {
1035
+ const data = JSON.parse(line.slice(6));
1036
+ if (data.type !== "connected")
1037
+ yield data;
1038
+ } catch {}
1039
+ }
1040
+ }
1041
+ }
1042
+ } finally {
1043
+ reader.releaseLock();
1044
+ }
756
1045
  }
757
1046
  }
758
1047
 
759
- // src/project-registration/adoption-validation.ts
760
- var VALIDATION_KEYS = [
761
- "valid",
762
- "resource_kind",
763
- "target_id",
764
- "source_receipt_id",
765
- "accepted_receipt_id",
766
- "source_outcome",
767
- "created_at",
768
- "current_revision",
769
- "accepted_result_digest"
770
- ];
771
- function isRecord(value) {
772
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
773
- }
774
- function isNonEmptyString(value) {
775
- return typeof value === "string" && value.length > 0;
776
- }
777
- function hasExactKeys(value, expected) {
778
- const actual = Object.keys(value).sort();
779
- const wanted = [...expected].sort();
780
- return actual.length === wanted.length && actual.every((key, index) => key === wanted[index]);
781
- }
782
- function adoptionRejected(message) {
783
- throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_ADOPTION_REJECTED", `TODOS_PROJECT_REGISTRATION_ADOPTION_REJECTED: ${message}`);
784
- }
785
- function assertTodosPriorRegistrationAdoptionValidationEnvelope(value, input) {
786
- if (!isRecord(input) || !hasExactKeys(input, [
787
- "source_request",
788
- "source_receipt",
789
- "current_record"
790
- ])) {
791
- adoptionRejected("prior-adoption validation input is incomplete");
1048
+ class AgentsResource {
1049
+ client;
1050
+ constructor(client) {
1051
+ this.client = client;
792
1052
  }
793
- const request = input["source_request"];
794
- const receipt = input["source_receipt"];
795
- const current = input["current_record"];
796
- if (!isRecord(request) || !isRecord(receipt) || !isRecord(current)) {
797
- adoptionRejected("prior-adoption validation input records are incomplete");
1053
+ async list(options) {
1054
+ return this.client._get("/api/agents", buildQuery(options || {}));
798
1055
  }
799
- const resourceKind = request["resource_kind"];
800
- const sourceOutcome = receipt["outcome"];
801
- const acceptedReceiptId = sourceOutcome === "accepted" ? receipt["receipt_id"] : sourceOutcome === "duplicate_of_accepted" ? receipt["duplicate_of_receipt_id"] : null;
802
- if (resourceKind !== "project" && resourceKind !== "task_list" || request["direction"] !== "forward" || sourceOutcome !== "accepted" && sourceOutcome !== "duplicate_of_accepted" || !isNonEmptyString(acceptedReceiptId) || !isNonEmptyString(receipt["receipt_id"]) || !isNonEmptyString(receipt["target_id"]) || !isNonEmptyString(receipt["result_revision"]) || !isNonEmptyString(receipt["result_digest"]) || !isNonEmptyString(current["id"]) || !isNonEmptyString(current["created_at"]) || !isNonEmptyString(current["updated_at"]) || receipt["authority"] !== "todos" || receipt["route"] !== request["authority_route"] || receipt["package_version"] !== request["package_version"] || receipt["authority_id"] !== request["authority_id"] || receipt["tenant_id"] !== request["tenant_id"] || receipt["corpus_id"] !== request["corpus_id"] || receipt["operation_id"] !== request["operation_id"] || receipt["step_id"] !== request["step_id"] || receipt["resource_kind"] !== resourceKind || receipt["direction"] !== "forward" || receipt["idempotency_key"] !== request["idempotency_key"] || receipt["request_digest"] !== request["request_digest"] || receipt["precondition_digest"] !== request["precondition_digest"] || receipt["accepted_receipt_id"] !== null || receipt["target_id"] !== current["id"] || receipt["result_revision"] !== current["created_at"]) {
803
- adoptionRejected("prior-adoption validation input does not carry one complete accepted receipt and current target incarnation");
1056
+ async register(data) {
1057
+ return this.client._post("/api/agents", data);
804
1058
  }
805
- if (sourceOutcome === "accepted" && receipt["duplicate_of_receipt_id"] !== null || sourceOutcome === "duplicate_of_accepted" && receipt["duplicate_of_receipt_id"] !== acceptedReceiptId) {
806
- adoptionRejected("prior-adoption validation source receipt lineage is incomplete");
1059
+ async fullRegister(data) {
1060
+ return this.client._post("/api/agents", data);
807
1061
  }
808
- if (!isRecord(value) || !hasExactKeys(value, ["validation"]) || !isRecord(value["validation"]) || !hasExactKeys(value["validation"], VALIDATION_KEYS)) {
809
- adoptionRejected("prior-adoption validation response envelope is incomplete");
1062
+ async update(id, data) {
1063
+ return this.client._patch(`/api/agents/${id}`, data);
810
1064
  }
811
- const validation = value["validation"];
812
- if (validation["valid"] !== true || validation["resource_kind"] !== resourceKind || validation["target_id"] !== current["id"] || validation["source_receipt_id"] !== receipt["receipt_id"] || validation["accepted_receipt_id"] !== acceptedReceiptId || validation["source_outcome"] !== sourceOutcome || validation["created_at"] !== current["created_at"] || validation["current_revision"] !== current["updated_at"] || validation["accepted_result_digest"] !== receipt["result_digest"]) {
813
- adoptionRejected("prior-adoption validation response does not prove the exact accepted receipt and current target");
1065
+ async delete(id) {
1066
+ return this.client._delete(`/api/agents/${id}`);
1067
+ }
1068
+ async bulkDelete(ids) {
1069
+ return this.client._post("/api/agents/bulk", { ids, action: "delete" });
1070
+ }
1071
+ async me(name) {
1072
+ return this.client._get("/api/agents/me", `?name=${encodeURIComponent(name)}`);
1073
+ }
1074
+ async queue(agentId) {
1075
+ return this.client._get(`/api/agents/${encodeURIComponent(agentId)}/queue`);
1076
+ }
1077
+ async team(agentId) {
1078
+ return this.client._get(`/api/agents/${encodeURIComponent(agentId)}/team`);
1079
+ }
1080
+ async orgChart() {
1081
+ return this.client._get("/api/org");
814
1082
  }
815
- return validation;
816
1083
  }
817
1084
 
818
- // src/sdk/v1.generated.ts
819
- class ApiError extends Error {
820
- status;
821
- body;
822
- constructor(status, message, body) {
823
- super(message);
824
- this.status = status;
825
- this.body = body;
826
- this.name = "ApiError";
1085
+ class ProjectsResource {
1086
+ client;
1087
+ constructor(client) {
1088
+ this.client = client;
1089
+ }
1090
+ async list(options) {
1091
+ return this.client._get("/api/projects", buildQuery(options || {}));
1092
+ }
1093
+ async create(data) {
1094
+ return this.client._post("/api/projects", data);
1095
+ }
1096
+ async delete(id) {
1097
+ return this.client._delete(`/api/projects/${id}`);
1098
+ }
1099
+ async bulkDelete(ids) {
1100
+ return this.client._post("/api/projects/bulk", { ids, action: "delete" });
827
1101
  }
828
1102
  }
829
1103
 
830
- class TodosV1Client {
831
- baseUrl;
832
- apiKey;
833
- fetchImpl;
834
- baseHeaders;
835
- constructor(options) {
836
- if (!options.baseUrl)
837
- throw new Error("TodosV1Client requires a baseUrl.");
838
- this.baseUrl = options.baseUrl.replace(/\/$/, "");
839
- this.apiKey = options.apiKey;
840
- this.fetchImpl = options.fetch ?? globalThis.fetch;
841
- this.baseHeaders = options.headers ?? {};
1104
+ class PlansResource {
1105
+ client;
1106
+ constructor(client) {
1107
+ this.client = client;
842
1108
  }
843
- async request(method, path, opts) {
844
- const url = new URL(this.baseUrl + path);
845
- if (opts.query) {
846
- for (const [key, value] of Object.entries(opts.query)) {
847
- if (value !== undefined && value !== null)
848
- url.searchParams.set(key, String(value));
849
- }
850
- }
851
- const headers = { Accept: "application/json", ...this.baseHeaders, ...opts.init?.headers };
852
- if (this.apiKey)
853
- headers["x-api-key"] = this.apiKey;
854
- let payload;
855
- if (opts.body !== undefined) {
856
- headers["Content-Type"] = "application/json";
857
- payload = JSON.stringify(opts.body);
858
- }
859
- const response = await this.fetchImpl(url.toString(), { ...opts.init, method, headers, body: payload });
860
- const text = await response.text();
861
- const data = text ? (() => {
862
- try {
863
- return JSON.parse(text);
864
- } catch {
865
- return text;
866
- }
867
- })() : undefined;
868
- if (!response.ok) {
869
- throw new ApiError(response.status, `${method} ${path} failed: ${response.status}`, data);
870
- }
871
- return data;
872
- }
873
- async importSnapshot(body, init) {
874
- return this.request("POST", `/v1/import`, {
875
- body,
876
- query: undefined,
877
- init
878
- });
879
- }
880
- async listPlans(query, init) {
881
- return this.request("GET", `/v1/plans`, {
882
- body: undefined,
883
- query,
884
- init
885
- });
886
- }
887
- async createPlan(body, init) {
888
- return this.request("POST", `/v1/plans`, {
889
- body,
890
- query: undefined,
891
- init
892
- });
893
- }
894
- async getPlan(id, init) {
895
- return this.request("GET", `/v1/plans/${encodeURIComponent(String(id))}`, {
896
- body: undefined,
897
- query: undefined,
898
- init
899
- });
1109
+ async list(projectId) {
1110
+ return this.client._get("/api/plans", projectId ? `?project_id=${projectId}` : "");
900
1111
  }
901
- async deletePlan(id, init) {
902
- return this.request("DELETE", `/v1/plans/${encodeURIComponent(String(id))}`, {
903
- body: undefined,
904
- query: undefined,
905
- init
906
- });
1112
+ async create(data) {
1113
+ return this.client._post("/api/plans", data);
907
1114
  }
908
- async updatePlan(id, body, init) {
909
- return this.request("PATCH", `/v1/plans/${encodeURIComponent(String(id))}`, {
910
- body,
911
- query: undefined,
912
- init
913
- });
1115
+ async get(id) {
1116
+ return this.client._get(`/api/plans/${id}`);
914
1117
  }
915
- async planPlanProjectLink(id, query, init) {
916
- return this.request("GET", `/v1/plans/${encodeURIComponent(String(id))}/project-link`, {
917
- body: undefined,
918
- query,
919
- init
920
- });
1118
+ async update(id, data) {
1119
+ return this.client._patch(`/api/plans/${id}`, data);
921
1120
  }
922
- async applyPlanProjectLink(id, body, init) {
923
- return this.request("POST", `/v1/plans/${encodeURIComponent(String(id))}/project-link`, {
924
- body,
925
- query: undefined,
926
- init
927
- });
1121
+ async delete(id) {
1122
+ return this.client._delete(`/api/plans/${id}`);
928
1123
  }
929
- async rollbackPlanProjectLink(id, body, init) {
930
- return this.request("POST", `/v1/plans/${encodeURIComponent(String(id))}/project-link/rollback`, {
931
- body,
932
- query: undefined,
933
- init
934
- });
1124
+ async bulkDelete(ids) {
1125
+ return this.client._post("/api/plans/bulk", { ids, action: "delete" });
935
1126
  }
936
- async admitPrGroup(body, init) {
937
- return this.request("POST", `/v1/pr-groups/admit`, {
938
- body,
939
- query: undefined,
940
- init
941
- });
1127
+ }
1128
+
1129
+ class OrgsResource {
1130
+ client;
1131
+ constructor(client) {
1132
+ this.client = client;
942
1133
  }
943
- async getPrGroupState(id, init) {
944
- return this.request("GET", `/v1/pr-groups/${encodeURIComponent(String(id))}`, {
945
- body: undefined,
946
- query: undefined,
947
- init
948
- });
1134
+ async list() {
1135
+ return this.client._get("/api/orgs");
949
1136
  }
950
- async getPrGroupEvents(id, query, init) {
951
- return this.request("GET", `/v1/pr-groups/${encodeURIComponent(String(id))}/events`, {
952
- body: undefined,
953
- query,
954
- init
955
- });
1137
+ async create(data) {
1138
+ return this.client._post("/api/orgs", data);
956
1139
  }
957
- async appendPrGroupEvent(id, body, init) {
958
- return this.request("POST", `/v1/pr-groups/${encodeURIComponent(String(id))}/events`, {
959
- body,
960
- query: undefined,
961
- init
962
- });
1140
+ async update(id, data) {
1141
+ return this.client._patch(`/api/orgs/${id}`, data);
963
1142
  }
964
- async recoverPrGroup(id, body, init) {
965
- return this.request("POST", `/v1/pr-groups/${encodeURIComponent(String(id))}/recover`, {
966
- body,
967
- query: undefined,
968
- init
969
- });
1143
+ async delete(id) {
1144
+ return this.client._delete(`/api/orgs/${id}`);
970
1145
  }
971
- async getProjectRegistrationCapability(init) {
972
- return this.request("GET", `/v1/project-registration/capability`, {
973
- body: undefined,
974
- query: undefined,
975
- init
976
- });
1146
+ }
1147
+
1148
+ class WebhooksResource {
1149
+ client;
1150
+ constructor(client) {
1151
+ this.client = client;
977
1152
  }
978
- async compensateProjectRegistrationResource(body, init) {
979
- return this.request("POST", `/v1/project-registration/compensate`, {
980
- body,
981
- query: undefined,
982
- init
983
- });
1153
+ async list() {
1154
+ return this.client._get("/api/webhooks");
984
1155
  }
985
- async createProjectRegistrationResource(body, init) {
986
- return this.request("POST", `/v1/project-registration/create`, {
987
- body,
988
- query: undefined,
989
- init
990
- });
1156
+ async create(data) {
1157
+ return this.client._post("/api/webhooks", data);
991
1158
  }
992
- async readExactProjectRegistrationResource(body, init) {
993
- return this.request("POST", `/v1/project-registration/read-exact`, {
994
- body,
995
- query: undefined,
996
- init
997
- });
1159
+ async delete(id) {
1160
+ return this.client._delete(`/api/webhooks/${id}`);
998
1161
  }
999
- async lookupProjectRegistrationReceipt(body, init) {
1000
- return this.request("POST", `/v1/project-registration/receipts/lookup`, {
1001
- body,
1002
- query: undefined,
1003
- init
1004
- });
1162
+ }
1163
+
1164
+ class TemplatesResource {
1165
+ client;
1166
+ constructor(client) {
1167
+ this.client = client;
1005
1168
  }
1006
- async listProjectRegistrationResources(query, init) {
1007
- return this.request("GET", `/v1/project-registration/resources`, {
1008
- body: undefined,
1009
- query,
1010
- init
1011
- });
1169
+ async list() {
1170
+ return this.client._get("/api/templates");
1012
1171
  }
1013
- async validatePriorRegistrationAdoption(body, init) {
1014
- const response = await this.request("POST", `/v1/project-registration/validate-prior-adoption`, {
1015
- body,
1016
- query: undefined,
1017
- init
1018
- });
1019
- return {
1020
- validation: assertTodosPriorRegistrationAdoptionValidationEnvelope(response, body)
1021
- };
1172
+ async create(data) {
1173
+ return this.client._post("/api/templates", data);
1022
1174
  }
1023
- async verifyInverseProjectRegistrationResource(body, init) {
1024
- return this.request("POST", `/v1/project-registration/verify-inverse`, {
1025
- body,
1026
- query: undefined,
1027
- init
1028
- });
1175
+ async delete(id) {
1176
+ return this.client._delete(`/api/templates/${id}`);
1029
1177
  }
1030
- async listProjects(init) {
1031
- return this.request("GET", `/v1/projects`, {
1032
- body: undefined,
1033
- query: undefined,
1034
- init
1035
- });
1178
+ }
1179
+
1180
+ class PrGroupsResource {
1181
+ client;
1182
+ constructor(client) {
1183
+ this.client = client;
1036
1184
  }
1037
- async createProject(body, init) {
1038
- return this.request("POST", `/v1/projects`, {
1039
- body,
1040
- query: undefined,
1041
- init
1042
- });
1185
+ async admit(input) {
1186
+ return this.client._post("/api/pr-groups/admit", input);
1043
1187
  }
1044
- async getProject(id, init) {
1045
- return this.request("GET", `/v1/projects/${encodeURIComponent(String(id))}`, {
1046
- body: undefined,
1047
- query: undefined,
1048
- init
1049
- });
1188
+ async get(groupId) {
1189
+ const result = await this.client._get(`/api/pr-groups/${encodeURIComponent(groupId)}`);
1190
+ return result.view;
1050
1191
  }
1051
- async deleteProject(id, init) {
1052
- return this.request("DELETE", `/v1/projects/${encodeURIComponent(String(id))}`, {
1053
- body: undefined,
1054
- query: undefined,
1055
- init
1056
- });
1192
+ async events(groupId, options = {}) {
1193
+ const result = await this.client._get(`/api/pr-groups/${encodeURIComponent(groupId)}/events`, buildQuery({
1194
+ limit: options.limit,
1195
+ after_sequence: options.after_sequence
1196
+ }));
1197
+ return result.history;
1057
1198
  }
1058
- async updateProject(id, body, init) {
1059
- return this.request("PATCH", `/v1/projects/${encodeURIComponent(String(id))}`, {
1060
- body,
1061
- query: undefined,
1062
- init
1063
- });
1199
+ async append(input) {
1200
+ return this.client._post(`/api/pr-groups/${encodeURIComponent(input.group_id)}/events`, input);
1064
1201
  }
1065
- async renameProject(id, body, init) {
1066
- return this.request("POST", `/v1/projects/${encodeURIComponent(String(id))}/rename`, {
1067
- body,
1068
- query: undefined,
1069
- init
1070
- });
1202
+ async recover(input) {
1203
+ return this.client._post(`/api/pr-groups/${encodeURIComponent(input.group_id)}/recover`, input);
1071
1204
  }
1072
- async planProjectTaskListEnsure(id, init) {
1073
- return this.request("GET", `/v1/projects/${encodeURIComponent(String(id))}/task-list/ensure`, {
1074
- body: undefined,
1075
- query: undefined,
1076
- init
1205
+ }
1206
+
1207
+ class TodosClient {
1208
+ baseUrl;
1209
+ timeout;
1210
+ maxRetries;
1211
+ retryDelay;
1212
+ explicitApiKey;
1213
+ constructedApiKey;
1214
+ pinnedAuthority;
1215
+ tasks;
1216
+ agents;
1217
+ projects;
1218
+ plans;
1219
+ orgs;
1220
+ webhooks;
1221
+ templates;
1222
+ prGroups;
1223
+ constructor(options = {}) {
1224
+ const resolved = resolveTodosSdkTransport({
1225
+ ...options.baseUrl !== undefined ? { baseUrl: options.baseUrl } : {},
1226
+ ...options.apiKey !== undefined ? { apiKey: options.apiKey } : {}
1077
1227
  });
1228
+ this.baseUrl = resolved.baseUrl;
1229
+ this.timeout = options.timeout ?? 1e4;
1230
+ this.explicitApiKey = options.apiKey;
1231
+ this.constructedApiKey = resolved.apiKey;
1232
+ this.pinnedAuthority = Boolean(options.baseUrl);
1233
+ this.maxRetries = options.maxRetries ?? 0;
1234
+ this.retryDelay = options.retryDelay ?? 1000;
1235
+ this.tasks = new TasksResource(this);
1236
+ this.agents = new AgentsResource(this);
1237
+ this.projects = new ProjectsResource(this);
1238
+ this.plans = new PlansResource(this);
1239
+ this.orgs = new OrgsResource(this);
1240
+ this.webhooks = new WebhooksResource(this);
1241
+ this.templates = new TemplatesResource(this);
1242
+ this.prGroups = new PrGroupsResource(this);
1078
1243
  }
1079
- async ensureProjectTaskList(id, body, init) {
1080
- return this.request("POST", `/v1/projects/${encodeURIComponent(String(id))}/task-list/ensure`, {
1081
- body,
1082
- query: undefined,
1083
- init
1084
- });
1244
+ static fromEnv(apiKey) {
1245
+ return new TodosClient(apiKey === undefined ? {} : { apiKey });
1085
1246
  }
1086
- async rollbackProjectTaskListEnsure(id, body, init) {
1087
- return this.request("POST", `/v1/projects/${encodeURIComponent(String(id))}/task-list/rollback`, {
1088
- body,
1089
- query: undefined,
1090
- init
1091
- });
1247
+ async _fetchRaw(url, init) {
1248
+ const controller = new AbortController;
1249
+ const timer = setTimeout(() => controller.abort(), this.timeout);
1250
+ try {
1251
+ const headers = this._buildHeaders(init?.headers);
1252
+ return await fetch(url, { ...init, headers, signal: controller.signal });
1253
+ } finally {
1254
+ clearTimeout(timer);
1255
+ }
1092
1256
  }
1093
- async findTaskGitRefs(ref, init) {
1094
- return this.request("GET", `/v1/refs/${encodeURIComponent(String(ref))}`, {
1095
- body: undefined,
1096
- query: undefined,
1097
- init
1098
- });
1257
+ currentApiKey() {
1258
+ if (this.explicitApiKey !== undefined)
1259
+ return this.explicitApiKey;
1260
+ if (this.pinnedAuthority)
1261
+ return this.constructedApiKey;
1262
+ try {
1263
+ const fresh = resolveTodosSdkTransport({ notice: () => {} }).apiKey;
1264
+ if (fresh)
1265
+ return fresh;
1266
+ } catch {}
1267
+ return this.constructedApiKey;
1099
1268
  }
1100
- async getStats(init) {
1101
- return this.request("GET", `/v1/stats`, {
1102
- body: undefined,
1103
- query: undefined,
1104
- init
1105
- });
1269
+ get apiKey() {
1270
+ return this.currentApiKey();
1106
1271
  }
1107
- async listTaskLists(query, init) {
1108
- return this.request("GET", `/v1/task-lists`, {
1109
- body: undefined,
1110
- query,
1111
- init
1112
- });
1272
+ _buildHeaders(existing) {
1273
+ const headers = { "Content-Type": "application/json" };
1274
+ const apiKey = this.currentApiKey();
1275
+ if (apiKey)
1276
+ headers["x-api-key"] = apiKey;
1277
+ if (existing) {
1278
+ if (existing instanceof Headers) {
1279
+ existing.forEach((v, k) => {
1280
+ headers[k] = v;
1281
+ });
1282
+ } else if (Array.isArray(existing)) {
1283
+ for (const [k, v] of existing)
1284
+ headers[k] = v;
1285
+ } else {
1286
+ Object.assign(headers, existing);
1287
+ }
1288
+ }
1289
+ return headers;
1113
1290
  }
1114
- async createTaskList(body, init) {
1115
- return this.request("POST", `/v1/task-lists`, {
1116
- body,
1117
- query: undefined,
1118
- init
1119
- });
1291
+ async _fetchWithRetry(path, init) {
1292
+ let lastError = null;
1293
+ const maxAttempts = this.maxRetries + 1;
1294
+ for (let attempt = 0;attempt < maxAttempts; attempt++) {
1295
+ try {
1296
+ return await this._fetch(path, init);
1297
+ } catch (e) {
1298
+ lastError = e;
1299
+ if (e instanceof TodosAPIError && e.status < 500 && e.status !== 429)
1300
+ throw e;
1301
+ if (e instanceof TodosUnauthorizedError || e instanceof TodosNotFoundError || e instanceof TodosConflictError)
1302
+ throw e;
1303
+ if (attempt < maxAttempts - 1) {
1304
+ const delay = this.retryDelay * Math.pow(2, attempt);
1305
+ if (e instanceof TodosRateLimitError) {
1306
+ await this._sleep(e.retryAfter * 1000);
1307
+ } else {
1308
+ await this._sleep(delay);
1309
+ }
1310
+ }
1311
+ }
1312
+ }
1313
+ throw lastError || new Error("Request failed after retries");
1120
1314
  }
1121
- async getTaskList(id, init) {
1122
- return this.request("GET", `/v1/task-lists/${encodeURIComponent(String(id))}`, {
1123
- body: undefined,
1124
- query: undefined,
1125
- init
1126
- });
1315
+ async _fetch(path, init) {
1316
+ const controller = new AbortController;
1317
+ const timer = setTimeout(() => controller.abort(), this.timeout);
1318
+ try {
1319
+ const url = `${this.baseUrl}${path}`;
1320
+ const headers = this._buildHeaders(init?.headers);
1321
+ const res = await fetch(url, { ...init, headers, signal: controller.signal });
1322
+ if (!res.ok) {
1323
+ const body = await res.json().catch(() => ({ error: res.statusText }));
1324
+ const message = body.error || `HTTP ${res.status}: ${res.statusText}`;
1325
+ if (res.status === 401)
1326
+ throw new TodosUnauthorizedError(message, body);
1327
+ if (res.status === 404)
1328
+ throw new TodosNotFoundError(message, body);
1329
+ if (res.status === 409)
1330
+ throw new TodosConflictError(message, body);
1331
+ if (res.status === 429) {
1332
+ const retryAfter = parseInt(res.headers.get("retry-after") || "60", 10);
1333
+ throw new TodosRateLimitError(message, retryAfter, body);
1334
+ }
1335
+ throw new TodosAPIError(message, res.status, res.statusText, body);
1336
+ }
1337
+ if (res.status === 204)
1338
+ return null;
1339
+ const text = await res.text();
1340
+ if (text.length === 0)
1341
+ return null;
1342
+ return JSON.parse(text);
1343
+ } catch (e) {
1344
+ if (e instanceof DOMException && e.name === "AbortError") {
1345
+ throw new TodosTimeoutError(this.timeout);
1346
+ }
1347
+ throw e;
1348
+ } finally {
1349
+ clearTimeout(timer);
1350
+ }
1127
1351
  }
1128
- async deleteTaskList(id, init) {
1129
- return this.request("DELETE", `/v1/task-lists/${encodeURIComponent(String(id))}`, {
1130
- body: undefined,
1131
- query: undefined,
1132
- init
1133
- });
1352
+ async _get(path, query = "") {
1353
+ return this._fetchWithRetry(`${path}${query}`);
1134
1354
  }
1135
- async updateTaskList(id, body, init) {
1136
- return this.request("PATCH", `/v1/task-lists/${encodeURIComponent(String(id))}`, {
1137
- body,
1138
- query: undefined,
1139
- init
1355
+ async _post(path, body) {
1356
+ return this._fetchWithRetry(path, {
1357
+ method: "POST",
1358
+ body: body ? JSON.stringify(body) : undefined
1140
1359
  });
1141
1360
  }
1142
- async applyTaskManifest(body, init) {
1143
- return this.request("POST", `/v1/task-manifest/apply`, {
1144
- body,
1145
- query: undefined,
1146
- init
1361
+ async _patch(path, body) {
1362
+ return this._fetchWithRetry(path, {
1363
+ method: "PATCH",
1364
+ body: JSON.stringify(body)
1147
1365
  });
1148
1366
  }
1149
- async lookupTaskManifestBinding(body, init) {
1150
- return this.request("POST", `/v1/task-manifest/bindings/lookup`, {
1151
- body,
1152
- query: undefined,
1153
- init
1154
- });
1367
+ async _delete(path) {
1368
+ return this._fetchWithRetry(path, { method: "DELETE" });
1155
1369
  }
1156
- async getTaskManifestCapability(init) {
1157
- return this.request("GET", `/v1/task-manifest/capability`, {
1158
- body: undefined,
1159
- query: undefined,
1160
- init
1161
- });
1370
+ _sleep(ms) {
1371
+ return new Promise((resolve) => setTimeout(resolve, ms));
1162
1372
  }
1163
- async compensateTaskManifest(body, init) {
1164
- return this.request("POST", `/v1/task-manifest/compensate`, {
1165
- body,
1166
- query: undefined,
1167
- init
1168
- });
1373
+ async getHealth() {
1374
+ return this._get("/api/health");
1169
1375
  }
1170
- async readExactTaskManifest(body, init) {
1171
- return this.request("POST", `/v1/task-manifest/read-exact`, {
1172
- body,
1173
- query: undefined,
1174
- init
1175
- });
1376
+ async isAlive() {
1377
+ try {
1378
+ await this._get("/api/stats");
1379
+ return true;
1380
+ } catch {
1381
+ return false;
1382
+ }
1176
1383
  }
1177
- async applyTaskSubtreeTransfer(body, init) {
1178
- return this.request("POST", `/v1/task-subtree-transfer/apply`, {
1179
- body,
1180
- query: undefined,
1181
- init
1182
- });
1384
+ async getStats() {
1385
+ return this._get("/api/stats");
1183
1386
  }
1184
- async getTaskSubtreeTransferCapability(init) {
1185
- return this.request("GET", `/v1/task-subtree-transfer/capability`, {
1186
- body: undefined,
1187
- query: undefined,
1188
- init
1189
- });
1387
+ async getReport(options) {
1388
+ return this._get("/api/report", buildQuery({ days: options?.days, project_id: options?.projectId }));
1190
1389
  }
1191
- async inspectTaskSubtreeTransfer(body, init) {
1192
- return this.request("POST", `/v1/task-subtree-transfer/inspect`, {
1193
- body,
1194
- query: undefined,
1195
- init
1196
- });
1390
+ async doctor() {
1391
+ return this._get("/api/doctor");
1197
1392
  }
1198
- async readExactTaskSubtreeTransfer(body, init) {
1199
- return this.request("POST", `/v1/task-subtree-transfer/read-exact`, {
1200
- body,
1201
- query: undefined,
1202
- init
1203
- });
1393
+ async activity(limit) {
1394
+ return this._get("/api/activity", limit ? `?limit=${limit}` : "");
1204
1395
  }
1205
- async rollbackTaskSubtreeTransfer(body, init) {
1206
- return this.request("POST", `/v1/task-subtree-transfer/rollback`, {
1207
- body,
1208
- query: undefined,
1209
- init
1210
- });
1396
+ async listTasks(filter = {}) {
1397
+ return this.tasks.list(filter);
1211
1398
  }
1212
- async listTasks(query, init) {
1213
- return this.request("GET", `/v1/tasks`, {
1214
- body: undefined,
1215
- query,
1216
- init
1217
- });
1399
+ async getTask(id, options) {
1400
+ return this.tasks.get(id, options);
1218
1401
  }
1219
- async createTask(body, init) {
1220
- return this.request("POST", `/v1/tasks`, {
1221
- body,
1222
- query: undefined,
1223
- init
1224
- });
1402
+ async createTask(data) {
1403
+ return this.tasks.create(data);
1225
1404
  }
1226
- async getTask(id, init) {
1227
- return this.request("GET", `/v1/tasks/${encodeURIComponent(String(id))}`, {
1228
- body: undefined,
1229
- query: undefined,
1230
- init
1231
- });
1405
+ async updateTask(id, data) {
1406
+ return this.tasks.update(id, data);
1232
1407
  }
1233
- async deleteTask(id, init) {
1234
- return this.request("DELETE", `/v1/tasks/${encodeURIComponent(String(id))}`, {
1235
- body: undefined,
1236
- query: undefined,
1237
- init
1238
- });
1408
+ async deleteTask(id) {
1409
+ await this.tasks.delete(id);
1239
1410
  }
1240
- async updateTask(id, body, init) {
1241
- return this.request("PATCH", `/v1/tasks/${encodeURIComponent(String(id))}`, {
1242
- body,
1243
- query: undefined,
1244
- init
1245
- });
1411
+ async startTask(id, agentId) {
1412
+ return this.tasks.start(id, agentId);
1246
1413
  }
1247
- async listTaskComments(id, query, init) {
1248
- return this.request("GET", `/v1/tasks/${encodeURIComponent(String(id))}/comments`, {
1249
- body: undefined,
1250
- query,
1251
- init
1252
- });
1414
+ async completeTask(id, agentId) {
1415
+ return this.tasks.complete(id, agentId);
1253
1416
  }
1254
- async createTaskComment(id, body, init) {
1255
- return this.request("POST", `/v1/tasks/${encodeURIComponent(String(id))}/comments`, {
1256
- body,
1257
- query: undefined,
1258
- init
1259
- });
1417
+ async failTask(id, options = {}) {
1418
+ return this.tasks.fail(id, options);
1260
1419
  }
1261
- async completeTask(id, body, init) {
1262
- return this.request("POST", `/v1/tasks/${encodeURIComponent(String(id))}/complete`, {
1263
- body,
1264
- query: undefined,
1265
- init
1266
- });
1420
+ async logProgress(taskId, message, pctComplete, agentId) {
1421
+ return this.tasks.logProgress(taskId, message, pctComplete, agentId);
1267
1422
  }
1268
- async failTask(id, body, init) {
1269
- return this.request("POST", `/v1/tasks/${encodeURIComponent(String(id))}/fail`, {
1270
- body,
1271
- query: undefined,
1272
- init
1273
- });
1423
+ async getStatus(projectId, agentId) {
1424
+ return this.tasks.status({ project_id: projectId, agent_id: agentId });
1274
1425
  }
1275
- async listTaskGitRefs(id, init) {
1276
- return this.request("GET", `/v1/tasks/${encodeURIComponent(String(id))}/refs`, {
1277
- body: undefined,
1278
- query: undefined,
1279
- init
1280
- });
1426
+ async getActiveWork(projectId) {
1427
+ const res = await this.tasks.active(projectId);
1428
+ return res.active;
1281
1429
  }
1282
- async linkTaskGitRef(id, body, init) {
1283
- return this.request("POST", `/v1/tasks/${encodeURIComponent(String(id))}/refs`, {
1284
- body,
1285
- query: undefined,
1286
- init
1287
- });
1430
+ async getTasksChangedSince(since, projectId) {
1431
+ return this.tasks.changedSince(since, projectId);
1288
1432
  }
1289
- async handoffStaleTaskLock(id, body, init) {
1290
- return this.request("POST", `/v1/tasks/${encodeURIComponent(String(id))}/stale-lock-handoff`, {
1291
- body,
1292
- query: undefined,
1293
- init
1294
- });
1433
+ async getStaleTasks(minutes, projectId) {
1434
+ return this.tasks.stale({ minutes, project_id: projectId });
1295
1435
  }
1296
- async startTask(id, init) {
1297
- return this.request("POST", `/v1/tasks/${encodeURIComponent(String(id))}/start`, {
1298
- body: undefined,
1299
- query: undefined,
1300
- init
1301
- });
1436
+ async getContext(options = {}) {
1437
+ return this.tasks.context(options);
1302
1438
  }
1303
- async listTemplates(query, init) {
1304
- return this.request("GET", `/v1/templates`, {
1305
- body: undefined,
1306
- query,
1307
- init
1308
- });
1439
+ async exportTasks(filter = {}) {
1440
+ return this.tasks.export(filter);
1309
1441
  }
1310
- async createTemplate(body, init) {
1311
- return this.request("POST", `/v1/templates`, {
1312
- body,
1313
- query: undefined,
1314
- init
1315
- });
1442
+ async claimNextTask(agentId, projectId) {
1443
+ return this.tasks.claim(agentId, projectId);
1316
1444
  }
1317
- async getTemplate(id, init) {
1318
- return this.request("GET", `/v1/templates/${encodeURIComponent(String(id))}`, {
1319
- body: undefined,
1320
- query: undefined,
1321
- init
1322
- });
1445
+ async getTaskHistory(id, options) {
1446
+ return this.tasks.getHistory(id, options);
1323
1447
  }
1324
- async deleteTemplate(id, init) {
1325
- return this.request("DELETE", `/v1/templates/${encodeURIComponent(String(id))}`, {
1326
- body: undefined,
1327
- query: undefined,
1328
- init
1329
- });
1448
+ async getTaskAttachments(id) {
1449
+ return this.tasks.getAttachments(id);
1330
1450
  }
1331
- async updateTemplate(id, body, init) {
1332
- return this.request("PATCH", `/v1/templates/${encodeURIComponent(String(id))}`, {
1333
- body,
1334
- query: undefined,
1335
- init
1336
- });
1451
+ async getTaskProgress(id, options) {
1452
+ return this.tasks.getProgress(id, options);
1453
+ }
1454
+ async* subscribeToStream(options = {}) {
1455
+ yield* this.tasks.subscribe(options);
1456
+ }
1457
+ async getProjects() {
1458
+ return this.projects.list();
1337
1459
  }
1338
1460
  }
1461
+ function createClient(options) {
1462
+ return new TodosClient(options);
1463
+ }
1339
1464
  export {
1465
+ resolveTodosSdkTransport,
1466
+ createTodosV1Client,
1340
1467
  createClient,
1468
+ __resetTodosSdkLocalNotice,
1341
1469
  TodosV1Client,
1342
1470
  ApiError as TodosV1ApiError,
1343
1471
  TodosUnauthorizedError,
@@ -1346,5 +1474,6 @@ export {
1346
1474
  TodosNotFoundError,
1347
1475
  TodosConflictError,
1348
1476
  TodosClient,
1349
- TodosAPIError
1477
+ TodosAPIError,
1478
+ TODOS_LOCAL_SERVE_URL
1350
1479
  };