@todoist/automations-sdk 0.0.0-stage → 1.0.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.
@@ -0,0 +1,226 @@
1
+ import { z } from "zod";
2
+ //#region ../core/dist/esm/constants/usage-billing.constants.js
3
+ const USAGE_BILLING_DENIAL_CODES = [
4
+ "usage_billing_insufficient_credit",
5
+ "usage_billing_no_billing_account",
6
+ "usage_billing_plan_required",
7
+ "usage_billing_billing_paused"
8
+ ];
9
+ const USAGE_BILLING_TEMPORARILY_UNAVAILABLE_CODE = "usage_billing_temporarily_unavailable";
10
+ const USAGE_BILLING_UNAVAILABLE_CODE = "usage_billing_unavailable";
11
+ function isUsageBillingDenialCode(code) {
12
+ return typeof code === "string" && USAGE_BILLING_DENIAL_CODES.includes(code);
13
+ }
14
+ //#endregion
15
+ //#region ../core/dist/esm/types/automation.types.js
16
+ const AUTOMATION_CONNECTION_STATUSES = [
17
+ "ready",
18
+ "almost-ready",
19
+ "not-ready"
20
+ ];
21
+ const AUTOMATION_VISIBILITIES = ["private", "workspace"];
22
+ const AUTOMATION_DISABLED_REASONS = ["creator-left-workspace", "workspace-plan-lapsed"];
23
+ //#endregion
24
+ //#region ../core/dist/esm/schemas/automation-draft-message.schema.js
25
+ const saveMessageSchema = z.object({
26
+ role: z.string(),
27
+ parts: z.unknown(),
28
+ order: z.number(),
29
+ messageId: z.string()
30
+ });
31
+ //#endregion
32
+ //#region ../core/dist/esm/schemas/chat.schema.js
33
+ const chatConversationSchema = z.object({
34
+ id: z.string(),
35
+ title: z.string().nullable(),
36
+ archivedAt: z.string().nullable(),
37
+ isArchived: z.boolean(),
38
+ todoistUserId: z.string(),
39
+ workspaceId: z.string().nullable(),
40
+ createdAt: z.string(),
41
+ updatedAt: z.string()
42
+ });
43
+ const chatMessageSchema = saveMessageSchema;
44
+ const getConversationsResponseSchema = z.array(chatConversationSchema);
45
+ const getMessagesResponseSchema = z.array(chatMessageSchema);
46
+ const chatCompactionStatusSchema = z.enum([
47
+ "starting",
48
+ "complete",
49
+ "failed"
50
+ ]);
51
+ const chatCompactionBlockerSchema = z.enum([
52
+ "none",
53
+ "insufficient_messages",
54
+ "trailing_unanswered_user",
55
+ "incomplete_tool_interaction"
56
+ ]);
57
+ const chatCompactionResultStatusSchema = z.enum([
58
+ "compacted",
59
+ "skipped",
60
+ "stale",
61
+ "failed"
62
+ ]);
63
+ const chatCompactionStatusDataSchema = z.object({
64
+ status: chatCompactionStatusSchema,
65
+ conversationId: z.string(),
66
+ compactionId: z.string(),
67
+ message: z.string()
68
+ });
69
+ const chatCompactionTelemetryDataSchema = z.object({
70
+ kind: z.literal("telemetry"),
71
+ phase: z.enum(["snapshot", "post-compaction"]),
72
+ conversationId: z.string(),
73
+ compactionId: z.string().nullable(),
74
+ triggerMessages: z.number().int().positive(),
75
+ triggerTokens: z.number().int().positive(),
76
+ hardMaxTokens: z.number().int().positive(),
77
+ preserveRecentTurns: z.number().int().positive(),
78
+ messageCountAfterCutoff: z.number().int().nonnegative(),
79
+ estimatedTokens: z.number().int().nonnegative(),
80
+ thresholdProgressPercent: z.number().int().min(0).max(100),
81
+ shouldCompact: z.boolean(),
82
+ boundaryEligible: z.boolean(),
83
+ blocker: chatCompactionBlockerSchema,
84
+ attempted: z.boolean(),
85
+ resultStatus: chatCompactionResultStatusSchema.nullable(),
86
+ messagesSummarized: z.number().int().nonnegative().nullable(),
87
+ cutoffOrder: z.number().int().positive().nullable()
88
+ });
89
+ const chatCompactionDataSchema = z.union([chatCompactionStatusDataSchema, chatCompactionTelemetryDataSchema]);
90
+ z.object({
91
+ type: z.literal("data-compaction"),
92
+ data: chatCompactionDataSchema
93
+ });
94
+ const chatConversationTitleUpdatedDataSchema = z.object({
95
+ kind: z.literal("conversation-title-updated"),
96
+ conversationId: z.string(),
97
+ title: z.string()
98
+ });
99
+ z.object({
100
+ type: z.literal("data-conversation"),
101
+ data: chatConversationTitleUpdatedDataSchema
102
+ });
103
+ const CHAT_ACKNOWLEDGEMENT_PART_TYPE = "data-acknowledgement";
104
+ const chatAcknowledgementMessageSchema = z.string().trim().min(1).max(300);
105
+ const shownChatAcknowledgementDataSchema = z.object({
106
+ status: z.literal("shown"),
107
+ message: chatAcknowledgementMessageSchema
108
+ });
109
+ z.discriminatedUnion("status", [
110
+ z.object({ status: z.literal("pending") }),
111
+ shownChatAcknowledgementDataSchema,
112
+ z.object({ status: z.literal("skipped") })
113
+ ]);
114
+ const shownChatAcknowledgementDataPartSchema = z.object({
115
+ type: z.literal(CHAT_ACKNOWLEDGEMENT_PART_TYPE),
116
+ id: z.string().optional(),
117
+ data: shownChatAcknowledgementDataSchema
118
+ });
119
+ function isShownChatAcknowledgement(part) {
120
+ return typeof part === "object" && part !== null && "type" in part && part.type === "data-acknowledgement" && shownChatAcknowledgementDataPartSchema.safeParse(part).success;
121
+ }
122
+ const chatUsagePhaseSchema = z.enum(["streaming", "final"]);
123
+ const chatUsageMessageMetadataSchema = z.object({
124
+ kind: z.literal("usage"),
125
+ phase: chatUsagePhaseSchema,
126
+ modelId: z.string(),
127
+ providerNames: z.array(z.string()).optional(),
128
+ inputTokens: z.number().int().nonnegative(),
129
+ noCacheInputTokens: z.number().int().nonnegative(),
130
+ cacheReadInputTokens: z.number().int().nonnegative(),
131
+ cacheWriteInputTokens: z.number().int().nonnegative(),
132
+ outputTokens: z.number().int().nonnegative(),
133
+ totalTokens: z.number().int().nonnegative(),
134
+ inputCostMicroDollars: z.number().int().nonnegative(),
135
+ outputCostMicroDollars: z.number().int().nonnegative(),
136
+ totalCostMicroDollars: z.number().int().nonnegative(),
137
+ cacheWrite1hCostMicroDollars: z.number().int().nonnegative().default(0),
138
+ searchCostMicroDollars: z.number().int().nonnegative(),
139
+ nestedLlmCostMicroDollars: z.number().int().nonnegative(),
140
+ estimatedUserInputTokens: z.number().int().nonnegative(),
141
+ estimatedUserInputCostMicroDollars: z.number().int().nonnegative(),
142
+ estimatedBaselineInputTokens: z.number().int().nonnegative(),
143
+ estimatedBaselineInputCostMicroDollars: z.number().int().nonnegative()
144
+ });
145
+ z.object({
146
+ type: z.string(),
147
+ messageMetadata: chatUsageMessageMetadataSchema
148
+ });
149
+ const chatUsagePreviewSchema = z.object({
150
+ kind: z.literal("preview"),
151
+ modelId: z.string(),
152
+ estimatedBaselineInputTokens: z.number().int().nonnegative(),
153
+ estimatedBaselineInputCostMicroDollars: z.number().int().nonnegative(),
154
+ excludesToolSchemaOverhead: z.boolean()
155
+ });
156
+ const chatStreamRequestErrorResponseSchema = z.object({
157
+ message: z.string(),
158
+ statusCode: z.number().int(),
159
+ code: z.enum([
160
+ ...USAGE_BILLING_DENIAL_CODES,
161
+ USAGE_BILLING_TEMPORARILY_UNAVAILABLE_CODE,
162
+ USAGE_BILLING_UNAVAILABLE_CODE
163
+ ]).optional().catch(void 0),
164
+ execution_started: z.boolean().optional()
165
+ });
166
+ function getChatBillingRefusal(error) {
167
+ if (!(error instanceof Error) || !error.message.trim().startsWith("{")) return null;
168
+ let body;
169
+ try {
170
+ body = JSON.parse(error.message);
171
+ } catch {
172
+ return null;
173
+ }
174
+ const parsed = chatStreamRequestErrorResponseSchema.safeParse(body);
175
+ if (!parsed.success) return null;
176
+ if (isUsageBillingDenialCode(parsed.data.code)) return {
177
+ message: parsed.data.message,
178
+ code: parsed.data.code
179
+ };
180
+ if (parsed.data.statusCode === 402 && isBodyWithoutCodeOrReason(body)) return {
181
+ message: parsed.data.message,
182
+ code: void 0
183
+ };
184
+ return null;
185
+ }
186
+ function isBodyWithoutCodeOrReason(body) {
187
+ return typeof body === "object" && body !== null && !("code" in body) && !("reason" in body);
188
+ }
189
+ const chatConversationStatusSchema = z.enum([
190
+ "active",
191
+ "archived",
192
+ "all"
193
+ ]);
194
+ const getChatConversationsQuerySchema = z.object({
195
+ status: chatConversationStatusSchema.default("active"),
196
+ limit: z.coerce.number().int().positive().max(200).default(10)
197
+ });
198
+ const createChatConversationSchema = z.object({ title: z.string().nullable().optional() }).meta({ id: "CreateChatConversation" });
199
+ const createWorkspaceChatConversationSchema = createChatConversationSchema.extend({ visibility: z.enum(AUTOMATION_VISIBILITIES).optional() }).meta({ id: "CreateWorkspaceChatConversation" });
200
+ const updateChatConversationSchema = z.object({
201
+ title: z.string().nullable().optional(),
202
+ isArchived: z.boolean().optional()
203
+ }).refine((value) => value.title !== void 0 || value.isArchived !== void 0, { message: "At least one field must be provided" }).meta({ id: "UpdateChatConversation" });
204
+ //#endregion
205
+ //#region src/connectors/connection-selection.ts
206
+ function describeCandidateConnection({ id, accountDisplayLabel, mcpServerUrl }) {
207
+ const label = accountDisplayLabel ? connectionAccountLabel({
208
+ label: accountDisplayLabel,
209
+ mcpServerUrl
210
+ }) : mcpServerUrl && mcpServerHost(mcpServerUrl);
211
+ return label ? `${id} (${label})` : id;
212
+ }
213
+ function connectionAccountLabel({ label, mcpServerUrl }) {
214
+ if (!mcpServerUrl) return label;
215
+ const host = mcpServerHost(mcpServerUrl);
216
+ return label === host ? label : `${label} on ${host}`;
217
+ }
218
+ function mcpServerHost(mcpServerUrl) {
219
+ try {
220
+ return new URL(mcpServerUrl).host;
221
+ } catch {
222
+ return mcpServerUrl;
223
+ }
224
+ }
225
+ //#endregion
226
+ export { AUTOMATION_VISIBILITIES as _, chatStreamRequestErrorResponseSchema as a, createWorkspaceChatConversationSchema as c, getConversationsResponseSchema as d, getMessagesResponseSchema as f, AUTOMATION_DISABLED_REASONS as g, AUTOMATION_CONNECTION_STATUSES as h, chatConversationSchema as i, getChatBillingRefusal as l, updateChatConversationSchema as m, describeCandidateConnection as n, chatUsagePreviewSchema as o, isShownChatAcknowledgement as p, mcpServerHost as r, createChatConversationSchema as s, connectionAccountLabel as t, getChatConversationsQuerySchema as u };
@@ -0,0 +1,29 @@
1
+ //#region src/connectors/connection-selection.d.ts
2
+ /** An explicit connector-to-connection mapping supplied by the caller. */
3
+ type ConnectionSelection = {
4
+ connectorId: string;
5
+ connectionType?: 'mcp' | 'trigger';
6
+ connectionId: string;
7
+ };
8
+ /** One of several authorised connections the user has to choose between. */
9
+ type CandidateConnection = {
10
+ readonly id: string;
11
+ /** The account it belongs to, such as an email address, so the user can tell them apart. */
12
+ readonly accountDisplayLabel?: string;
13
+ /** The connection's own MCP server URL, for a tenant or self-hosted server. */
14
+ readonly mcpServerUrl?: string;
15
+ };
16
+ /** The id to pass back, with the account it belongs to when known. */
17
+ declare function describeCandidateConnection({ id, accountDisplayLabel, mcpServerUrl }: CandidateConnection): string;
18
+ /**
19
+ * An account label that tells connections on different MCP servers apart: a
20
+ * connection with its own server URL names the server's host after the account.
21
+ */
22
+ declare function connectionAccountLabel({ label, mcpServerUrl }: {
23
+ label: string;
24
+ mcpServerUrl?: string | null;
25
+ }): string;
26
+ /** The host of a connection's own MCP server URL. */
27
+ declare function mcpServerHost(mcpServerUrl: string): string;
28
+ //#endregion
29
+ export { mcpServerHost as a, describeCandidateConnection as i, ConnectionSelection as n, connectionAccountLabel as r, CandidateConnection as t };
@@ -0,0 +1,109 @@
1
+ import { n as AutomationsRequestError, t as AutomationsNetworkError } from "./request-error-DoLXUa2p.js";
2
+ //#region src/transport/http-client.ts
3
+ function buildUrl(baseUrl, path, query) {
4
+ const trimmedBase = baseUrl.replace(/\/+$/, "");
5
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
6
+ const url = new URL(`${trimmedBase}${normalizedPath}`);
7
+ if (query) for (const [key, value] of Object.entries(query)) {
8
+ if (value === void 0 || value === null) continue;
9
+ if (Array.isArray(value)) {
10
+ for (const item of value) url.searchParams.append(key, String(item));
11
+ continue;
12
+ }
13
+ url.searchParams.append(key, String(value));
14
+ }
15
+ return url.toString();
16
+ }
17
+ async function readResponseBody(response) {
18
+ const text = await response.text();
19
+ if (text.length === 0) return void 0;
20
+ try {
21
+ return JSON.parse(text);
22
+ } catch {
23
+ return text;
24
+ }
25
+ }
26
+ async function request(args) {
27
+ const { method, baseUrl, path, authToken, query, body, signal, headers: extraHeaders, isPublic, fetch: fetchImpl = fetch } = args;
28
+ const url = buildUrl(baseUrl, path, query);
29
+ const headers = {
30
+ Accept: "application/json",
31
+ ...extraHeaders
32
+ };
33
+ if (!isPublic) headers.Authorization = `Bearer ${authToken}`;
34
+ const init = {
35
+ method,
36
+ headers
37
+ };
38
+ if (body !== void 0) {
39
+ headers["Content-Type"] = "application/json";
40
+ init.body = JSON.stringify(body);
41
+ }
42
+ if (signal !== void 0) init.signal = signal;
43
+ let response;
44
+ try {
45
+ response = await fetchImpl(url, init);
46
+ } catch (error) {
47
+ if (signal?.aborted) throw error;
48
+ throw new AutomationsNetworkError("Request failed before a response was received", error);
49
+ }
50
+ const data = await readResponseBody(response);
51
+ if (!response.ok) {
52
+ const message = typeof data === "object" && data !== null && "message" in data ? String(data.message) : response.statusText || `Request failed with status ${response.status}`;
53
+ throw new AutomationsRequestError(message, response.status, data, response.headers.get("WWW-Authenticate") ?? void 0, response.headers.get("X-Request-Id") ?? void 0);
54
+ }
55
+ return data;
56
+ }
57
+ //#endregion
58
+ //#region src/clients/base-client.ts
59
+ function mergeHeaders$1(defaults, overrides) {
60
+ if (!defaults && !overrides) return void 0;
61
+ const merged = {};
62
+ if (defaults) for (const [key, value] of Object.entries(defaults)) merged[key.toLowerCase()] = value;
63
+ if (overrides) for (const [key, value] of Object.entries(overrides)) merged[key.toLowerCase()] = value;
64
+ return merged;
65
+ }
66
+ var BaseClient = class {
67
+ context;
68
+ constructor(context) {
69
+ this.context = context;
70
+ }
71
+ request(args) {
72
+ const normalizedPath = args.path.startsWith("/") ? args.path : `/${args.path}`;
73
+ return request({
74
+ method: args.method,
75
+ baseUrl: this.context.baseUrl,
76
+ path: `/v1${normalizedPath}`,
77
+ authToken: this.context.authToken,
78
+ query: args.query,
79
+ body: args.body,
80
+ signal: args.options?.signal,
81
+ headers: mergeHeaders$1(this.context.defaultHeaders, args.options?.headers),
82
+ isPublic: args.isPublic,
83
+ fetch: this.context.fetch
84
+ });
85
+ }
86
+ };
87
+ //#endregion
88
+ //#region src/clients/context.ts
89
+ const DEFAULT_BASE_URL = "https://automations.todoist.com/api";
90
+ const DEFAULT_TODOIST_BASE_URL = "https://api.todoist.com/api";
91
+ function mergeHeaders(defaults, overrides) {
92
+ if (!defaults && !overrides) return void 0;
93
+ const merged = {};
94
+ for (const headers of [defaults, overrides]) {
95
+ if (!headers) continue;
96
+ for (const [key, value] of Object.entries(headers)) merged[key.toLowerCase()] = value;
97
+ }
98
+ return merged;
99
+ }
100
+ function createClientContext(authToken, options = {}, apiOrigin = "automations") {
101
+ return {
102
+ authToken,
103
+ baseUrl: (apiOrigin === "todoist" ? options.todoistBaseUrl ?? "https://api.todoist.com/api" : options.baseUrl ?? "https://automations.todoist.com/api").replace(/\/+$/, ""),
104
+ defaultHeaders: mergeHeaders(options.defaultHeaders, apiOrigin === "todoist" ? options.todoistDefaultHeaders : options.automationsDefaultHeaders),
105
+ fetch: options.fetch
106
+ };
107
+ }
108
+ //#endregion
109
+ export { BaseClient as i, DEFAULT_TODOIST_BASE_URL as n, createClientContext as r, DEFAULT_BASE_URL as t };
@@ -0,0 +1,65 @@
1
+ //#region src/transport/http-client.d.ts
2
+ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
3
+ type QueryParams = Record<string, string | number | boolean | null | undefined | readonly string[] | readonly number[]>;
4
+ //#endregion
5
+ //#region src/clients/base-client.d.ts
6
+ type ClientContext = {
7
+ authToken: string;
8
+ baseUrl: string;
9
+ defaultHeaders?: Record<string, string>;
10
+ fetch?: typeof fetch;
11
+ };
12
+ type RequestOptions = {
13
+ signal?: AbortSignal;
14
+ headers?: Record<string, string>;
15
+ };
16
+ declare abstract class BaseClient {
17
+ protected readonly context: ClientContext;
18
+ constructor(context: ClientContext);
19
+ protected request<T = unknown>(args: {
20
+ method: HttpMethod;
21
+ path: string;
22
+ query?: QueryParams;
23
+ body?: unknown;
24
+ options?: RequestOptions;
25
+ /**
26
+ * Set to `true` for backend routes decorated with `@Public()` so the
27
+ * `Authorization` header is omitted. Defaults to `false`.
28
+ */
29
+ isPublic?: boolean;
30
+ }): Promise<T>;
31
+ }
32
+ //#endregion
33
+ //#region src/clients/context.d.ts
34
+ declare const DEFAULT_BASE_URL = "https://automations.todoist.com/api";
35
+ declare const DEFAULT_TODOIST_BASE_URL = "https://api.todoist.com/api";
36
+ type ClientApiOptions = {
37
+ baseUrl?: string;
38
+ todoistBaseUrl?: string;
39
+ /**
40
+ * Headers merged into every request this client makes. Useful for
41
+ * static values like `x-todoist-staging` that should follow the token
42
+ * for the lifetime of the instance. Per-call `RequestOptions.headers`
43
+ * take precedence on conflict.
44
+ */
45
+ defaultHeaders?: Record<string, string>;
46
+ /** Headers merged into Automations API requests after `defaultHeaders`. */
47
+ automationsDefaultHeaders?: Record<string, string>;
48
+ /** Headers merged into Todoist API requests after `defaultHeaders`. */
49
+ todoistDefaultHeaders?: Record<string, string>;
50
+ /**
51
+ * The `fetch` every request on both API origins goes through. Defaults to
52
+ * the global `fetch`. Lets a caller add per-request values, such as a new
53
+ * request ID, that a static `defaultHeaders` map cannot carry.
54
+ */
55
+ fetch?: typeof fetch;
56
+ };
57
+ /**
58
+ * Builds the immutable context shared by every sub-client of an API instance.
59
+ * Normalizes the base URL (strips trailing slashes) so callers can pass values
60
+ * with or without one. Shared by `AutomationsApi` and the admin SDK so both
61
+ * resolve tokens, base URLs and default headers identically.
62
+ */
63
+ declare function createClientContext(authToken: string, options?: ClientApiOptions, apiOrigin?: 'automations' | 'todoist'): ClientContext;
64
+ //#endregion
65
+ export { BaseClient as a, createClientContext as i, DEFAULT_BASE_URL as n, ClientContext as o, DEFAULT_TODOIST_BASE_URL as r, RequestOptions as s, ClientApiOptions as t };