teak-sdk 1.0.86 → 1.0.87

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.
package/dist/sdk.js CHANGED
@@ -1,12 +1,18 @@
1
- // ../convex/devUrls.ts
2
- var TEAK_DEV_DEPLOYMENT_NAME = "reminiscent-kangaroo-59";
3
- var DEFAULT_TEAK_DEV_CONVEX_URL = `https://${TEAK_DEV_DEPLOYMENT_NAME}.convex.cloud`;
4
- var DEFAULT_TEAK_DEV_CONVEX_SITE_URL = `https://${TEAK_DEV_DEPLOYMENT_NAME}.convex.site`;
5
- var isLocalDevelopmentHostname = (hostname) => {
6
- const normalizedHostname = hostname.trim().toLowerCase();
7
- return normalizedHostname === "localhost" || normalizedHostname === "127.0.0.1" || normalizedHostname.endsWith(".localhost");
1
+ // ../convex/shared/utils/formatBytes.ts
2
+ var BYTE_UNITS = ["B", "KB", "MB", "GB"];
3
+ var formatBytes = (bytes) => {
4
+ if (!(bytes && Number.isFinite(bytes) && bytes > 0)) {
5
+ return "0 B";
6
+ }
7
+ let value = bytes;
8
+ let unit = 0;
9
+ while (value >= 1024 && unit < BYTE_UNITS.length - 1) {
10
+ value /= 1024;
11
+ unit += 1;
12
+ }
13
+ const digits = unit > 0 && value < 10 ? 1 : 0;
14
+ return `${value.toFixed(digits)} ${BYTE_UNITS[unit]}`;
8
15
  };
9
-
10
16
  // ../convex/shared/boundedResponse.ts
11
17
  class ResponseSizeLimitError extends Error {
12
18
  }
@@ -40,28 +46,17 @@ var consumeResponseWithinLimit = async (response, maxBytes, consume) => {
40
46
  };
41
47
  var readResponseTextWithinLimit = (response, maxBytes) => consumeResponseWithinLimit(response, maxBytes, (boundedResponse) => boundedResponse.text());
42
48
 
43
- // ../convex/shared/workosResources.ts
44
- var WORKOS_RESOURCES = {
45
- api: "https://teakvault.com/api",
46
- mcp: "https://teakvault.com/mcp"
49
+ // ../convex/devUrls.ts
50
+ var TEAK_DEV_DEPLOYMENT_NAME = "reminiscent-kangaroo-59";
51
+ var DEFAULT_TEAK_DEV_CONVEX_URL = `https://${TEAK_DEV_DEPLOYMENT_NAME}.convex.cloud`;
52
+ var DEFAULT_TEAK_DEV_CONVEX_SITE_URL = `https://${TEAK_DEV_DEPLOYMENT_NAME}.convex.site`;
53
+ var isLocalDevelopmentHostname = (hostname) => {
54
+ const normalizedHostname = hostname.trim().toLowerCase();
55
+ return normalizedHostname === "localhost" || normalizedHostname === "127.0.0.1" || normalizedHostname.endsWith(".localhost");
47
56
  };
48
57
 
49
- // ../convex/client/authDiscovery.ts
50
- var OAUTH_SURFACES = [
51
- "cli",
52
- "raycast",
53
- "chrome",
54
- "firefox",
55
- "safari"
56
- ];
57
- var caches = new WeakMap;
58
- var object = (value) => {
59
- if (!value || typeof value !== "object" || Array.isArray(value)) {
60
- throw new Error("Invalid OAuth discovery document");
61
- }
62
- return value;
63
- };
64
- var loopback = (hostname) => isLocalDevelopmentHostname(hostname) || hostname === "[::1]";
58
+ // ../convex/shared/oauthUrl.ts
59
+ var isLoopbackHostname = (hostname) => isLocalDevelopmentHostname(hostname) || hostname === "[::1]";
65
60
  var privateHost = (hostname) => {
66
61
  const octets = hostname.split(".").map(Number);
67
62
  return hostname.endsWith(".localhost") || hostname.endsWith(".local") || hostname.startsWith("[") || octets.length === 4 && octets.every((n) => Number.isInteger(n) && n >= 0 && n <= 255) && (octets[0] === 0 || octets[0] === 10 || octets[0] === 100 && octets[1] >= 64 && octets[1] <= 127 || octets[0] === 127 || octets[0] === 169 && octets[1] === 254 || octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31 || octets[0] === 192 && octets[1] === 168);
@@ -71,22 +66,40 @@ var validateOAuthUrl = (raw, local = false) => {
71
66
  throw new Error("Missing OAuth discovery URL");
72
67
  }
73
68
  const url = new URL(raw);
74
- if (url.username || url.password || url.hash || url.protocol !== "https:" && !(local && url.protocol === "http:" && loopback(url.hostname)) || !local && loopback(url.hostname) || privateHost(url.hostname) && !(local && loopback(url.hostname))) {
69
+ if (url.username || url.password || url.hash || url.protocol !== "https:" && !(local && url.protocol === "http:" && isLoopbackHostname(url.hostname)) || !local && isLoopbackHostname(url.hostname) || privateHost(url.hostname) && !(local && isLoopbackHostname(url.hostname))) {
75
70
  throw new Error("Unsafe OAuth discovery URL");
76
71
  }
77
72
  return url;
78
73
  };
74
+
75
+ // ../convex/shared/workosResources.ts
76
+ var PRODUCTION_WORKOS_RESOURCE_ORIGIN = "https://teakvault.com";
77
+ var workosResourcesFor = (url) => {
78
+ const { origin } = new URL(url);
79
+ return Object.freeze({ api: `${origin}/api`, mcp: `${origin}/mcp` });
80
+ };
81
+ var PRODUCTION_WORKOS_RESOURCES = workosResourcesFor(PRODUCTION_WORKOS_RESOURCE_ORIGIN);
82
+
83
+ // ../convex/client/authDiscovery.ts
84
+ var OAUTH_SURFACES = ["cli", "raycast", "chrome", "firefox"];
85
+ var caches = new WeakMap;
86
+ var object = (value) => {
87
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
88
+ throw new Error("Invalid OAuth discovery document");
89
+ }
90
+ return value;
91
+ };
79
92
  function discoverAuthServer(siteUrl, options = {}) {
80
93
  const site = validateOAuthUrl(siteUrl, true);
81
- const siteIsLocal = loopback(site.hostname);
94
+ const siteIsLocal = isLoopbackHostname(site.hostname);
82
95
  const permittedLocalIssuer = options.localIssuer === undefined ? undefined : validateOAuthUrl(options.localIssuer, true);
83
- if (permittedLocalIssuer && !loopback(permittedLocalIssuer.hostname)) {
96
+ if (permittedLocalIssuer && !isLoopbackHostname(permittedLocalIssuer.hostname)) {
84
97
  throw new Error("Development issuer must use loopback");
85
98
  }
86
99
  const local = siteIsLocal || permittedLocalIssuer !== undefined;
87
100
  const validateUrl = (raw) => {
88
101
  const url = validateOAuthUrl(raw, local);
89
- if (loopback(url.hostname) && !siteIsLocal && url.origin !== permittedLocalIssuer?.origin) {
102
+ if (isLoopbackHostname(url.hostname) && !siteIsLocal && url.origin !== permittedLocalIssuer?.origin) {
90
103
  throw new Error("Unapproved loopback OAuth server");
91
104
  }
92
105
  return url;
@@ -113,7 +126,7 @@ function discoverAuthServer(siteUrl, options = {}) {
113
126
  const read = async (url) => {
114
127
  const response = await fetchImpl(url, {
115
128
  method: "GET",
116
- credentials: "omit",
129
+ ...{ credentials: "omit" },
117
130
  redirect: "error",
118
131
  headers: { Accept: "application/json" },
119
132
  signal: controller.signal
@@ -158,7 +171,7 @@ function discoverAuthServer(siteUrl, options = {}) {
158
171
  throw new Error("Missing OAuth client registration");
159
172
  }
160
173
  }
161
- if (resource.resource !== WORKOS_RESOURCES.mcp) {
174
+ if (resource.resource !== PRODUCTION_WORKOS_RESOURCES.mcp && resource.resource !== workosResourcesFor(site.origin).mcp) {
162
175
  throw new Error("OAuth resource mismatch");
163
176
  }
164
177
  return Object.freeze({
@@ -196,7 +209,7 @@ var base64Url = (bytes) => {
196
209
  };
197
210
  var randomBase64Url = (byteLength) => base64Url(crypto.getRandomValues(new Uint8Array(byteLength)));
198
211
  var createPkceChallenge = async (verifier) => base64Url(new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier))));
199
- var apiResource = (auth) => new URL("/api", auth.resource).href;
212
+ var apiResource = (auth) => workosResourcesFor(auth.resource).api;
200
213
  var createConnectAuthorizeUrl = (auth, params) => {
201
214
  const url = new URL(auth.authorizationEndpoint);
202
215
  url.searchParams.set("response_type", "code");
@@ -211,7 +224,7 @@ var createConnectAuthorizeUrl = (auth, params) => {
211
224
  };
212
225
  var send = (url, init, local) => fetch(validateOAuthUrl(url, local).href, {
213
226
  ...init,
214
- credentials: "omit",
227
+ ...{ credentials: "omit" },
215
228
  redirect: "error",
216
229
  signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
217
230
  });
@@ -304,8 +317,55 @@ var fetchConnectOwnerId = async (apiUrl, accessToken, options = {}) => {
304
317
  // ../convex/shared/markdown.ts
305
318
  var MARKDOWN_CONTENT_MAX_BYTES = 512 * 1024;
306
319
 
320
+ // ../files-protocol/src/decisions.ts
321
+ var CLEF_MODEL_ID = "@cf/cloudflare/clef";
322
+ var CLEF_FLASH_MODEL_ID = "@cf/cloudflare/clef-flash";
323
+ var CLEF_INPUT_USD_PER_MILLION = {
324
+ [CLEF_MODEL_ID]: 0.24,
325
+ [CLEF_FLASH_MODEL_ID]: 0.038
326
+ };
327
+ var RELEVANCE_LEVELS = [
328
+ "Not related",
329
+ "Loosely related",
330
+ "Relevant",
331
+ "Exactly what they are looking for"
332
+ ];
333
+ var RELEVANCE_MAX_SCORE = RELEVANCE_LEVELS.length - 1;
334
+ // ../files-protocol/src/embeddings.ts
335
+ var ASCII_TOKEN_COST = 1 / 3;
336
+ // ../files-protocol/src/storage.ts
337
+ var FILES_UPLOAD_MAX_BYTES = 100 * 1024 * 1024;
338
+ var FILES_SIGNED_URL_MIN_VALIDITY_SECONDS = 7 * 24 * 60 * 60;
339
+ var FILE_CACHE_MAX_AGE_SECONDS = FILES_SIGNED_URL_MIN_VALIDITY_SECONDS - 24 * 60 * 60;
340
+ var FILES_PRIVATE_CACHE_CONTROL = `private, max-age=${FILE_CACHE_MAX_AGE_SECONDS}, immutable`;
341
+ var FILES_EDGE_CACHE_CONTROL = `public, max-age=${FILE_CACHE_MAX_AGE_SECONDS}, immutable`;
342
+ var FILES_DEV_KEY_PREFIX = "dev/";
343
+ var filesUserKeyRoot = (envPrefix) => `${envPrefix}users/`;
344
+ var filesExportKeyRoot = (envPrefix) => `${envPrefix}exports/`;
345
+ var FILES_OBJECT_KEY_ROOTS = ["", FILES_DEV_KEY_PREFIX].flatMap((envPrefix) => [filesUserKeyRoot(envPrefix), filesExportKeyRoot(envPrefix)]);
346
+
347
+ // ../files-protocol/src/inspect.ts
348
+ var FILES_INSPECT_MAX_ARCHIVE_BYTES = 25 * 1024 * 1024;
349
+ var FILES_INSPECT_MAX_SOURCE_BYTES = 2 * 1024 * 1024;
350
+ // ../files-protocol/src/remoteAsset.ts
351
+ var FILES_REMOTE_ASSET_MAX_BYTES = {
352
+ image: 20 * 1024 * 1024,
353
+ video: 50 * 1024 * 1024
354
+ };
355
+ // ../files-protocol/src/workflows.ts
356
+ var WORKFLOW_RPC_MAX_TTL_SECONDS = 5 * 60;
357
+ var WORKFLOW_RPC_MAX_BODY_BYTES = 4 * 1024 * 1024;
358
+ var RPC_KINDS = new Set(["action", "mutation", "query"]);
359
+ // ../files-protocol/src/index.ts
360
+ var FILES_UPLOAD_MAX_TTL_SECONDS = 15 * 60;
361
+ var FILES_OP_MAX_TTL_SECONDS = 15 * 60;
362
+ var FILES_OP_TTL_SECONDS = 10 * 60;
363
+ var FILES_TRANSCRIPT_MAX_BYTES = 512 * 1024;
364
+ var FILES_EXTRACTED_TEXT_MAX_CHARACTERS = 512 * 1024;
365
+ var FILES_EXTRACTED_TEXT_MAX_BYTES = 512 * 1024;
366
+ var FILES_IMPORT_PAGE_BYTES = 4 * 1024 * 1024;
367
+
307
368
  // ../convex/shared/fileFormats.ts
308
- var MAX_FILE_SIZE = 100 * 1024 * 1024;
309
369
  var GENERIC_MIME_TYPES = new Set([
310
370
  "",
311
371
  "application/binary",
@@ -764,6 +824,19 @@ var FILE_FORMATS = [
764
824
  preview: "audio"
765
825
  })
766
826
  ];
827
+ // ../convex/shared/tags.ts
828
+ var MAX_TAG_NAME_LENGTH = 64;
829
+ var TAG_OPERATION_STALL_MS = 2 * 60000;
830
+ var TAG_ERROR_MESSAGES = {
831
+ EMPTY_NAME: "Name the tag",
832
+ NAME_TOO_LONG: `Tags can be at most ${MAX_TAG_NAME_LENGTH} characters`,
833
+ NOT_FOUND: "Tag not found",
834
+ SAME_TAG: "Choose a different tag to merge into",
835
+ OPERATION_RUNNING: "Another tag change is still running",
836
+ OPERATION_NOT_FOUND: "Tag operation not found"
837
+ };
838
+ var HIDDEN_TAG_KEYS = ["secret", "hidden"];
839
+ var hiddenTagKeys = new Set(HIDDEN_TAG_KEYS);
767
840
  // ../convex/shared/constants.ts
768
841
  var cardTypes = [
769
842
  "text",
@@ -775,7 +848,9 @@ var cardTypes = [
775
848
  "palette",
776
849
  "quote"
777
850
  ];
851
+ var TRASH_RETENTION_MS = 30 * 24 * 60 * 60 * 1000;
778
852
  var MULTIPART_UPLOAD_THRESHOLD = 8 * 1024 * 1024;
853
+ var API_IDEMPOTENCY_KEY_TTL_MS = 24 * 60 * 60 * 1000;
779
854
  var CARD_TYPES = [...cardTypes];
780
855
  var CARD_TYPE_REGISTRY = {
781
856
  text: {
@@ -830,25 +905,40 @@ var RESERVED_KEYWORDS = [
830
905
 
831
906
  // ../convex/client/sdk.ts
832
907
  var CARD_TYPES2 = cardTypes;
833
- var CARD_SORTS = ["newest", "oldest"];
908
+ var CARD_SORTS = ["newest", "oldest", "relevance", "shuffle"];
834
909
  var ERROR_CODES = [
835
910
  "AUTH_REQUIRED",
836
911
  "BAD_REQUEST",
912
+ "CARD_LIMIT_REACHED",
837
913
  "CONFIG_ERROR",
838
914
  "CONFLICT",
839
915
  "CONTENT_TOO_LARGE",
916
+ "FILE_TOO_LARGE",
917
+ "FOLDER_LIMIT",
918
+ "FORBIDDEN",
919
+ "IN_PROGRESS",
840
920
  "INTERNAL_ERROR",
841
921
  "INVALID_API_KEY",
842
922
  "INVALID_INPUT",
923
+ "INVALID_STORAGE_KEY",
843
924
  "INVALID_UTF8",
925
+ "LIMIT_REACHED",
844
926
  "METHOD_NOT_ALLOWED",
845
927
  "NETWORK_ERROR",
846
928
  "NOT_FOUND",
929
+ "NO_CONTENT",
930
+ "NOT_IN_TRASH",
847
931
  "PARSE_ERROR",
848
932
  "RATE_LIMITED",
849
933
  "REQUEST_FAILED",
934
+ "SERVICE_UNAVAILABLE",
935
+ "SMART_FOLDER",
936
+ "TAG_EXISTS",
937
+ "TAG_OPERATION_RUNNING",
850
938
  "TIMEOUT",
851
- "UNAUTHORIZED"
939
+ "TYPE_MISMATCH",
940
+ "UNAUTHORIZED",
941
+ "UNSUPPORTED_TYPE"
852
942
  ];
853
943
 
854
944
  class TeakApiError extends Error {
@@ -931,6 +1021,9 @@ var buildCardsSearchParams = (input) => {
931
1021
  if (input.tag?.trim()) {
932
1022
  search.set("tag", input.tag.trim());
933
1023
  }
1024
+ if (input.folder?.trim()) {
1025
+ search.set("folder", input.folder.trim());
1026
+ }
934
1027
  if (input.cursor?.trim()) {
935
1028
  search.set("cursor", input.cursor.trim());
936
1029
  }
@@ -943,8 +1036,20 @@ var buildCardsSearchParams = (input) => {
943
1036
  if (input.trashed) {
944
1037
  search.set("trashed", "true");
945
1038
  }
946
- if (input.sort === "oldest") {
947
- search.set("sort", "oldest");
1039
+ if (input.archived === "only" || input.archived === "include") {
1040
+ search.set("archived", input.archived);
1041
+ }
1042
+ if (input.similarTo?.trim()) {
1043
+ search.set("similarTo", input.similarTo.trim());
1044
+ }
1045
+ if (input.mode === "keyword" || input.mode === "semantic") {
1046
+ search.set("mode", input.mode);
1047
+ }
1048
+ if (input.sort === "oldest" || input.sort === "shuffle") {
1049
+ search.set("sort", input.sort);
1050
+ }
1051
+ if (input.sort === "shuffle" && typeof input.shuffleSeed === "number") {
1052
+ search.set("shuffleSeed", String(input.shuffleSeed));
948
1053
  }
949
1054
  if (typeof input.createdAfter === "number") {
950
1055
  search.set("createdAfter", String(input.createdAfter));
@@ -983,6 +1088,35 @@ var asPage = (value) => {
983
1088
  }
984
1089
  return value;
985
1090
  };
1091
+ var asFolder = (value) => {
1092
+ if (!isObject(value) || typeof value.id !== "string" || typeof value.name !== "string") {
1093
+ throw new TeakApiError("PARSE_ERROR", "Teak returned an unexpected folder payload.");
1094
+ }
1095
+ return value;
1096
+ };
1097
+ var asFolders = (value) => {
1098
+ if (!(isObject(value) && Array.isArray(value.items))) {
1099
+ throw new TeakApiError("PARSE_ERROR");
1100
+ }
1101
+ return { items: value.items.map(asFolder) };
1102
+ };
1103
+ var asFolderCards = (value) => {
1104
+ if (!(isObject(value) && isStringArray(value.updatedIds) && isStringArray(value.failedIds))) {
1105
+ throw new TeakApiError("PARSE_ERROR");
1106
+ }
1107
+ return {
1108
+ updatedIds: value.updatedIds,
1109
+ failedIds: value.failedIds,
1110
+ folderLimitIds: isStringArray(value.folderLimitIds) ? value.folderLimitIds : []
1111
+ };
1112
+ };
1113
+ var asTagOperation = (value) => {
1114
+ if (!isObject(value) || typeof value.id !== "string" || typeof value.status !== "string") {
1115
+ throw new TeakApiError("PARSE_ERROR", "Teak returned an unexpected tag operation payload.");
1116
+ }
1117
+ const { operationId: _operationId, ...operation } = value;
1118
+ return operation;
1119
+ };
986
1120
  var asTags = (value) => {
987
1121
  if (!(isObject(value) && Array.isArray(value.items))) {
988
1122
  throw new TeakApiError("PARSE_ERROR");
@@ -1000,6 +1134,24 @@ var asCreate = (value) => {
1000
1134
  }
1001
1135
  return value;
1002
1136
  };
1137
+ var asRefresh = (value) => {
1138
+ if (!isObject(value) || typeof value.cardId !== "string" || value.status !== "queued") {
1139
+ throw new TeakApiError("PARSE_ERROR");
1140
+ }
1141
+ return value;
1142
+ };
1143
+ var asSavedPdf = (value) => {
1144
+ if (!isObject(value) || typeof value.cardId !== "string" || typeof value.created !== "boolean") {
1145
+ throw new TeakApiError("PARSE_ERROR");
1146
+ }
1147
+ return { cardId: value.cardId, created: value.created };
1148
+ };
1149
+ var asCardContent = (value) => {
1150
+ if (!isObject(value) || typeof value.cardId !== "string" || typeof value.text !== "string" || typeof value.charCount !== "number" || typeof value.truncated !== "boolean" || !["document", "transcript", "content"].includes(String(value.kind))) {
1151
+ throw new TeakApiError("PARSE_ERROR");
1152
+ }
1153
+ return value;
1154
+ };
1003
1155
  var asMe = (value) => {
1004
1156
  if (!(isObject(value) && isObject(value.data)) || typeof value.data.id !== "string") {
1005
1157
  throw new TeakApiError("PARSE_ERROR");
@@ -1074,6 +1226,25 @@ var createTeakClient = (options) => {
1074
1226
  const qs = (params) => buildCardsSearchParams(params);
1075
1227
  const idempotency = (requestOptions) => requestOptions?.idempotencyKey ? { "Idempotency-Key": requestOptions.idempotencyKey } : {};
1076
1228
  const cardPath = (id) => `/v1/cards/${encodeURIComponent(id)}`;
1229
+ const folderPath = (id) => `/v1/folders/${encodeURIComponent(id)}`;
1230
+ const putFile = async (uploadUrl, bytes, mimeType) => {
1231
+ const headers = new Headers({ "Content-Type": mimeType });
1232
+ const byteLength = byteLengthForBody(bytes);
1233
+ if (byteLength !== null) {
1234
+ headers.set("Content-Length", String(byteLength));
1235
+ }
1236
+ const response = await fetchImpl(uploadUrl, {
1237
+ body: bytes,
1238
+ headers,
1239
+ method: "PUT"
1240
+ });
1241
+ if (!response.ok) {
1242
+ throw new TeakApiError("REQUEST_FAILED", `Upload failed with status ${response.status}`, { status: response.status });
1243
+ }
1244
+ return { fileEtag: response.headers.get("etag") ?? undefined };
1245
+ };
1246
+ const createThumbnailUpload = (id, input) => request(`${cardPath(id)}/thumbnail/upload`, { body: JSON.stringify(input), method: "POST" }, asUpload);
1247
+ const applyThumbnail = (id, fileKey) => request(`${cardPath(id)}/thumbnail`, { body: JSON.stringify({ fileKey }), method: "PUT" }, asCard);
1077
1248
  return {
1078
1249
  cards: {
1079
1250
  bulk: (operation, items, requestOptions) => request("/v1/cards/bulk", {
@@ -1091,13 +1262,43 @@ var createTeakClient = (options) => {
1091
1262
  headers: idempotency(requestOptions),
1092
1263
  method: "POST"
1093
1264
  }, asCreate),
1265
+ content: (id) => request(`${cardPath(id)}/content`, { method: "GET" }, asCardContent),
1094
1266
  delete: (id, deleteOptions) => request(`${cardPath(id)}${deleteOptions?.permanent ? "?permanent=true" : ""}`, { method: "DELETE" }, () => null),
1095
1267
  duplicate: (url) => request(`/v1/cards/duplicate?${new URLSearchParams({ url }).toString()}`, { method: "GET" }, (v) => isObject(v) && typeof v.cardId === "string" ? v.cardId : null),
1096
1268
  favorites: (input = {}) => request(`/v1/cards?${qs({ include: "content,metadata", ...input, favorited: true })}`, { method: "GET" }, asPage),
1097
1269
  get: (id) => request(cardPath(id), { method: "GET" }, asCard),
1270
+ links: (id) => request(cardPath(id), { method: "GET" }, (value) => {
1271
+ const card = asCard(value);
1272
+ return {
1273
+ backlinks: card.backlinks ?? [],
1274
+ backlinksTruncated: card.backlinksTruncated === true,
1275
+ links: card.links ?? [],
1276
+ linksTruncated: card.linksTruncated === true
1277
+ };
1278
+ }),
1098
1279
  list: (input = {}) => request(`/v1/cards?${qs(input)}`, { method: "GET" }, asPage),
1280
+ createThumbnailUpload,
1281
+ refresh: (id) => request(`${cardPath(id)}/refresh`, { method: "POST" }, asRefresh),
1282
+ resetThumbnail: (id) => request(`${cardPath(id)}/thumbnail`, { method: "DELETE" }, asCard),
1099
1283
  restore: (id) => request(`${cardPath(id)}/restore`, { method: "POST" }, () => null),
1284
+ similar: (id, input = {}) => request(`/v1/cards?${qs({ include: "content,metadata", ...input, similarTo: id })}`, { method: "GET" }, asPage),
1100
1285
  search: (input = {}) => request(`/v1/cards?${qs({ include: "content,metadata", ...input })}`, { method: "GET" }, asPage),
1286
+ setArchived: (id, isArchived) => request(cardPath(id), { body: JSON.stringify({ isArchived }), method: "PATCH" }, asCard),
1287
+ setThumbnail: async (id, image, mimeType) => {
1288
+ if (isObject(image) && typeof image.fileKey === "string") {
1289
+ return applyThumbnail(id, image.fileKey);
1290
+ }
1291
+ const bytes = image;
1292
+ const fileSize = byteLengthForBody(bytes);
1293
+ if (fileSize === null || !mimeType) {
1294
+ throw new TeakApiError("INVALID_INPUT", "setThumbnail needs the image's bytes and its MIME type.");
1295
+ }
1296
+ const upload = await createThumbnailUpload(id, { fileSize, mimeType });
1297
+ await putFile(upload.uploadUrl, bytes, mimeType);
1298
+ return applyThumbnail(id, upload.fileKey);
1299
+ },
1300
+ savePdf: (id) => request(`${cardPath(id)}/save-pdf`, { method: "POST" }, asSavedPdf),
1301
+ setDone: (id, done) => request(`${cardPath(id)}/done`, { method: done ? "POST" : "DELETE" }, asCard),
1101
1302
  setFavorite: (id, isFavorited) => request(`${cardPath(id)}/favorite`, { body: JSON.stringify({ isFavorited }), method: "PATCH" }, asCard),
1102
1303
  update: (id, input) => request(cardPath(id), { body: JSON.stringify(input), method: "PATCH" }, asCard)
1103
1304
  },
@@ -1105,28 +1306,26 @@ var createTeakClient = (options) => {
1105
1306
  latest: () => request("/v1/exports/latest", { method: "GET" }, asLatestExport),
1106
1307
  start: () => request("/v1/exports", { method: "POST" }, (v) => v.job)
1107
1308
  },
1309
+ folders: {
1310
+ addCards: (id, cardIds) => request(`${folderPath(id)}/cards`, { body: JSON.stringify({ cardIds }), method: "POST" }, asFolderCards),
1311
+ create: (input) => request("/v1/folders", { body: JSON.stringify(input), method: "POST" }, asFolder),
1312
+ delete: (id) => request(folderPath(id), { method: "DELETE" }, () => null),
1313
+ get: (id) => request(folderPath(id), { method: "GET" }, asFolder),
1314
+ list: () => request("/v1/folders", { method: "GET" }, asFolders),
1315
+ removeCards: (id, cardIds) => request(`${folderPath(id)}/cards`, { body: JSON.stringify({ cardIds }), method: "DELETE" }, asFolderCards),
1316
+ update: (id, input) => request(folderPath(id), { body: JSON.stringify(input), method: "PATCH" }, asFolder)
1317
+ },
1108
1318
  me: () => request("/v1/me", { method: "GET" }, asMe),
1109
1319
  tags: {
1110
- list: () => request("/v1/tags", { method: "GET" }, asTags)
1320
+ delete: (tag) => request("/v1/tags/delete", { body: JSON.stringify({ tag }), method: "POST" }, asTagOperation),
1321
+ list: () => request("/v1/tags", { method: "GET" }, asTags),
1322
+ merge: (from, into) => request("/v1/tags/merge", { body: JSON.stringify({ from, into }), method: "POST" }, asTagOperation),
1323
+ operation: (id) => request(`/v1/tags/operations/${encodeURIComponent(id)}`, { method: "GET" }, asTagOperation),
1324
+ rename: (from, to) => request("/v1/tags/rename", { body: JSON.stringify({ from, to }), method: "POST" }, asTagOperation)
1111
1325
  },
1112
1326
  uploads: {
1113
1327
  create: (input) => request("/v1/uploads", { body: JSON.stringify(input), method: "POST" }, asUpload),
1114
- putFile: async (uploadUrl, bytes, mimeType) => {
1115
- const headers = new Headers({ "Content-Type": mimeType });
1116
- const byteLength = byteLengthForBody(bytes);
1117
- if (byteLength !== null) {
1118
- headers.set("Content-Length", String(byteLength));
1119
- }
1120
- const response = await fetchImpl(uploadUrl, {
1121
- body: bytes,
1122
- headers,
1123
- method: "PUT"
1124
- });
1125
- if (!response.ok) {
1126
- throw new TeakApiError("REQUEST_FAILED", `Upload failed with status ${response.status}`, { status: response.status });
1127
- }
1128
- return { fileEtag: response.headers.get("etag") ?? undefined };
1129
- }
1328
+ putFile
1130
1329
  }
1131
1330
  };
1132
1331
  };
@@ -1146,6 +1345,7 @@ export {
1146
1345
  disconnectConnectGrant,
1147
1346
  discoverAuthServer,
1148
1347
  fetchConnectOwnerId,
1348
+ formatBytes,
1149
1349
  isCardType,
1150
1350
  isStringList,
1151
1351
  normalizeLimit,
@@ -1,4 +1,5 @@
1
- export declare const OAUTH_SURFACES: readonly ["cli", "raycast", "chrome", "firefox", "safari"];
1
+ export { validateOAuthUrl } from "../shared/oauthUrl.js";
2
+ export declare const OAUTH_SURFACES: readonly ["cli", "raycast", "chrome", "firefox"];
2
3
  export type OAuthSurface = (typeof OAUTH_SURFACES)[number];
3
4
  export interface AuthDiscovery {
4
5
  readonly authorizationEndpoint: string;
@@ -8,7 +9,6 @@ export interface AuthDiscovery {
8
9
  readonly resource: string;
9
10
  readonly tokenEndpoint: string;
10
11
  }
11
- export declare const validateOAuthUrl: (raw: unknown, local?: boolean) => URL;
12
12
  export declare function discoverAuthServer(siteUrl: string, options?: {
13
13
  fetch?: typeof fetch;
14
14
  forceRefresh?: boolean;
@@ -1,12 +1,19 @@
1
+ export { formatBytes } from "../shared/utils/formatBytes.js";
1
2
  export { type AuthDiscovery, discoverAuthServer, type OAuthSurface, validateOAuthUrl, } from "./authDiscovery.js";
2
3
  export { type ConnectTokenResult, type ConnectTokens, createConnectAuthorizeUrl, createPkceChallenge, disconnectConnectGrant, fetchConnectOwnerId, randomBase64Url, requestConnectTokens, WORKOS_CONNECT_SCOPE, } from "./workosConnect.js";
3
4
  import { type CardType } from "../shared/constants.js";
4
5
  import type { FileKind, FilePreviewFacts } from "../shared/fileFormats.js";
6
+ import type { FOLDER_COLORS } from "../shared/folders.js";
5
7
  export type { CardType } from "../shared/constants.js";
6
8
  export declare const CARD_TYPES: readonly ["text", "link", "image", "video", "audio", "document", "palette", "quote"];
7
- export declare const CARD_SORTS: readonly ["newest", "oldest"];
8
- export declare const ERROR_CODES: readonly ["AUTH_REQUIRED", "BAD_REQUEST", "CONFIG_ERROR", "CONFLICT", "CONTENT_TOO_LARGE", "INTERNAL_ERROR", "INVALID_API_KEY", "INVALID_INPUT", "INVALID_UTF8", "METHOD_NOT_ALLOWED", "NETWORK_ERROR", "NOT_FOUND", "PARSE_ERROR", "RATE_LIMITED", "REQUEST_FAILED", "TIMEOUT", "UNAUTHORIZED"];
9
+ export declare const CARD_SORTS: readonly ["newest", "oldest", "relevance", "shuffle"];
10
+ export declare const ERROR_CODES: readonly ["AUTH_REQUIRED", "BAD_REQUEST", "CARD_LIMIT_REACHED", "CONFIG_ERROR", "CONFLICT", "CONTENT_TOO_LARGE", "FILE_TOO_LARGE", "FOLDER_LIMIT", "FORBIDDEN", "IN_PROGRESS", "INTERNAL_ERROR", "INVALID_API_KEY", "INVALID_INPUT", "INVALID_STORAGE_KEY", "INVALID_UTF8", "LIMIT_REACHED", "METHOD_NOT_ALLOWED", "NETWORK_ERROR", "NOT_FOUND", "NO_CONTENT", "NOT_IN_TRASH", "PARSE_ERROR", "RATE_LIMITED", "REQUEST_FAILED", "SERVICE_UNAVAILABLE", "SMART_FOLDER", "TAG_EXISTS", "TAG_OPERATION_RUNNING", "TIMEOUT", "TYPE_MISMATCH", "UNAUTHORIZED", "UNSUPPORTED_TYPE"];
9
11
  export type CardSort = (typeof CARD_SORTS)[number];
12
+ /**
13
+ * How a search finds cards: `hybrid` (the API's default) matches words and
14
+ * meaning, `keyword` words only, `semantic` meaning only.
15
+ */
16
+ export type SearchMode = "hybrid" | "keyword" | "semantic";
10
17
  export type TeakApiErrorCode = (typeof ERROR_CODES)[number];
11
18
  export type FetchLike = typeof fetch;
12
19
  export interface TokenProvider {
@@ -18,11 +25,26 @@ export interface Card {
18
25
  aiTags: string[];
19
26
  aiTranscript: string | null;
20
27
  appUrl: string;
28
+ /** When the card was archived, or null. */
29
+ archivedAt: number | null;
30
+ /** Bar heights 0-100: 45 for a grid card, 200 for a player. */
31
+ audioWaveform: {
32
+ detail: number[];
33
+ grid: number[];
34
+ } | null;
35
+ /** Your cards that link to this one, most recently updated first. */
36
+ backlinks?: CardLinkRef[];
37
+ backlinksTruncated?: boolean;
21
38
  colors: Array<{
22
39
  hex: string;
23
40
  name?: string;
24
41
  }>;
25
42
  compactUrl: string | null;
43
+ /**
44
+ * When the link was marked read, watched, bought or done, or null.
45
+ * Servers that predate done states leave it out.
46
+ */
47
+ completedAt?: number | null;
26
48
  content: string;
27
49
  createdAt: number;
28
50
  detailUrl: string | null;
@@ -33,10 +55,25 @@ export interface Card {
33
55
  filePreview: FilePreviewFacts | null;
34
56
  fileSize: number | null;
35
57
  fileUrl: string | null;
58
+ /** Ids of the folders the card is in. */
59
+ folderIds: string[];
36
60
  id: string;
61
+ /** Archived cards are hidden from the library but still count. */
62
+ isArchived: boolean;
37
63
  isDeleted: boolean;
38
64
  isFavorited: boolean;
65
+ /**
66
+ * Tagged `secret` or `hidden`: left out of lists and searches unless the
67
+ * search names one of those tags (`#secret`). Servers that
68
+ * predate hidden cards leave it out.
69
+ */
70
+ isHidden?: boolean;
39
71
  linkAuthor: string | null;
72
+ /**
73
+ * What a link is (`article`, `movie`, `product`…), or null. A plain
74
+ * string: new categories are added over time.
75
+ */
76
+ linkCategory?: string | null;
40
77
  linkFacts: Array<{
41
78
  label: string;
42
79
  value: string;
@@ -53,9 +90,23 @@ export interface Card {
53
90
  posterWidth?: number;
54
91
  posterHeight?: number;
55
92
  }>;
93
+ /**
94
+ * A product link's price: `amount` in the page's currency, `currency` an
95
+ * ISO 4217 code or null. Null for cards without a price.
96
+ */
97
+ linkPrice: {
98
+ amount: number;
99
+ currency: string | null;
100
+ } | null;
56
101
  linkPublishedAt: string | null;
57
102
  linkPublisher: string | null;
58
103
  linkSiteName: string | null;
104
+ /**
105
+ * Cards this card links to with `[[Title|teak:<id>]]`, in order. Only on
106
+ * single-card responses (`cards.get`, update, favorite, done, create).
107
+ */
108
+ links?: CardLinkRef[];
109
+ linksTruncated?: boolean;
59
110
  metadataDescription: string | null;
60
111
  metadataStatus: string | null;
61
112
  metadataTitle: string | null;
@@ -64,11 +115,43 @@ export interface Card {
64
115
  placeholderUrl: string | null;
65
116
  processingStatus: Record<string, unknown> | null;
66
117
  screenshotUrl: string | null;
118
+ /**
119
+ * The title of the page in `sourceUrl`. Servers that predate sources
120
+ * leave it out.
121
+ */
122
+ sourceTitle?: string | null;
123
+ /**
124
+ * The page the card was saved from, such as the page a quote was selected
125
+ * on. Servers that predate sources leave it out.
126
+ */
127
+ sourceUrl?: string | null;
67
128
  tags: string[];
129
+ /** True when linkPreviewImageUrl is an image chosen for the link. */
130
+ thumbnailOverridden: boolean;
68
131
  thumbnailUrl: string | null;
69
132
  type: CardType | string;
70
133
  updatedAt: number;
71
134
  url: string | null;
135
+ /** Hover preview strip, `frames * frameWidth` wide; the URL expires. */
136
+ videoScrub: {
137
+ frameHeight: number;
138
+ frameWidth: number;
139
+ frames: number;
140
+ url: string;
141
+ } | null;
142
+ }
143
+ /** A linked card; `missing` when it's in Trash, deleted or not yours. */
144
+ export interface CardLinkRef {
145
+ id: string;
146
+ missing: boolean;
147
+ title: string | null;
148
+ type: CardType | string | null;
149
+ }
150
+ export interface CardLinks {
151
+ backlinks: CardLinkRef[];
152
+ backlinksTruncated: boolean;
153
+ links: CardLinkRef[];
154
+ linksTruncated: boolean;
72
155
  }
73
156
  export type CardListItem = Partial<Card> & Pick<Card, "aiTags" | "appUrl" | "createdAt" | "id" | "isFavorited" | "tags" | "type" | "updatedAt">;
74
157
  export interface PageInfo {
@@ -78,6 +161,15 @@ export interface PageInfo {
78
161
  export interface CardsPage {
79
162
  items: CardListItem[];
80
163
  pageInfo: PageInfo;
164
+ /** With a query and `sort: "relevance"`: whether the top results were scored. */
165
+ relevanceRanked?: boolean;
166
+ /** With a query: the mode that ran (`keyword` when meaning search could not). */
167
+ searchMode?: SearchMode;
168
+ /**
169
+ * For similar cards: `pending` while the card is still being read, and
170
+ * `unavailable` for a card in Trash or not found.
171
+ */
172
+ similarity?: "ok" | "pending" | "unavailable";
81
173
  }
82
174
  export interface CardChangesResponse {
83
175
  deletedIds: string[];
@@ -88,21 +180,102 @@ export interface TagSummary {
88
180
  count: number;
89
181
  name: string;
90
182
  }
183
+ export type FolderColor = (typeof FOLDER_COLORS)[number];
184
+ export interface Folder {
185
+ /**
186
+ * Cards in a manual folder that are not archived or in Trash; null for a
187
+ * smart folder, whose cards are found when listed.
188
+ */
189
+ cardCount: number | null;
190
+ color: FolderColor | null;
191
+ createdAt: number;
192
+ id: string;
193
+ /** Manual folders hold the cards put in them; smart ones save a search. */
194
+ kind: "manual" | "smart";
195
+ name: string;
196
+ /** A smart folder's search; null for a manual folder. */
197
+ query: string | null;
198
+ updatedAt: number;
199
+ }
200
+ export type TagOperationKind = "rename" | "merge" | "delete";
201
+ export type TagOperationStatus = "running" | "completed" | "failed";
202
+ /** A library-wide tag rename, merge or delete, finished in the background. */
203
+ export interface TagOperation {
204
+ /** Cards rewritten so far. */
205
+ changed: number;
206
+ completedAt: number | null;
207
+ createdAt: number;
208
+ error: string | null;
209
+ /** The tag being changed, lowercased. */
210
+ from: string;
211
+ id: string;
212
+ kind: TagOperationKind;
213
+ /** Cards looked at so far. */
214
+ processed: number;
215
+ status: TagOperationStatus;
216
+ /** The new name (rename) or the tag merged into (merge). */
217
+ to: string | null;
218
+ /** Cards carrying the tag when the change started. */
219
+ total: number;
220
+ updatedAt: number;
221
+ }
222
+ export interface FolderCardsResult {
223
+ failedIds: string[];
224
+ /** Failed cards that are already in the most folders a card can be in. */
225
+ folderLimitIds: string[];
226
+ updatedIds: string[];
227
+ }
91
228
  export interface CreateCardInput {
229
+ /**
230
+ * Optional card type: `text`, `link`, `image`, `video`, `audio`,
231
+ * `document`, `palette` or `quote`. Set it to `text` to store raw Markdown
232
+ * exactly; omit it to keep automatic URL, quote, and palette detection.
233
+ * @default inferred
234
+ */
92
235
  cardType?: CardType;
236
+ /**
237
+ * Markdown or plain text body for the card. Prefer this for notes; use `url`
238
+ * when saving a link.
239
+ */
93
240
  content?: string;
241
+ /** The `ETag` the upload `PUT` returned, for a file card. */
94
242
  fileEtag?: string;
243
+ /** The `fileKey` from `uploads.create`, for a file card. */
95
244
  fileKey?: string;
245
+ /** The uploaded file's name, for a file card. */
96
246
  fileName?: string;
247
+ /** The uploaded file's size in bytes, for a file card. */
97
248
  fileSize?: number;
249
+ /** The uploaded file's MIME type, for a file card. */
98
250
  mimeType?: string;
251
+ /** Free-form notes attached to the card. */
99
252
  notes?: string | null;
253
+ /**
254
+ * A free label for what saved the card (extension, CLI, API, and so on),
255
+ * kept in its metadata. For the page the card came from, use `sourceUrl`.
256
+ */
100
257
  source?: string;
258
+ /** The page's title, beside `sourceUrl`; cut to 300 characters. */
259
+ sourceTitle?: string;
260
+ /**
261
+ * The page the card came from, such as the page a quote was selected on:
262
+ * an http or https URL of at most 2,048 characters. Unlike `url`, it never
263
+ * makes the card a link.
264
+ */
265
+ sourceUrl?: string;
266
+ /** Tags to apply on create. */
101
267
  tags?: string[];
268
+ /**
269
+ * URL to save as a link card. Teak may classify the type automatically when
270
+ * `cardType` is omitted.
271
+ */
102
272
  url?: string;
103
273
  }
104
274
  export interface UpdateCardInput {
105
275
  content?: string;
276
+ /** Replaces the card's folders; an empty list takes it out of every folder. */
277
+ folderIds?: string[];
278
+ isArchived?: boolean;
106
279
  metadataTitle?: string | null;
107
280
  notes?: string | null;
108
281
  /** AI tags to drop. `tags` replaces only the card's own tags. */
@@ -150,6 +323,11 @@ export interface CreateUploadInput {
150
323
  fileSize: number;
151
324
  mimeType: string;
152
325
  }
326
+ /** A link image upload: JPEG, PNG, WebP, GIF or AVIF, under 20 MB. */
327
+ export interface CreateThumbnailUploadInput {
328
+ fileSize: number;
329
+ mimeType: string;
330
+ }
153
331
  export interface CreateUploadResponse {
154
332
  expiresIn: number;
155
333
  fileKey: string;
@@ -163,8 +341,37 @@ export interface CreateCardResponse {
163
341
  cardId: string;
164
342
  status: "created";
165
343
  }
344
+ export interface RefreshCardResponse {
345
+ cardId: string;
346
+ requestedAt: number;
347
+ status: "queued";
348
+ }
349
+ /** An arXiv link's PDF saved as a document card (POST /v1/cards/:id/save-pdf). */
350
+ export interface SavedPdf {
351
+ /** The document card holding the PDF. */
352
+ cardId: string;
353
+ /** False when an earlier save already made that card. */
354
+ created: boolean;
355
+ }
356
+ /** A card's full stored text (GET /v1/cards/:id/content). */
357
+ export interface CardContent {
358
+ cardId: string;
359
+ /** Characters in the full stored text. */
360
+ charCount: number;
361
+ /**
362
+ * document: extracted from the card's file; transcript: its speech;
363
+ * content: a text or quote card's written text.
364
+ */
365
+ kind: "document" | "transcript" | "content";
366
+ text: string;
367
+ /** The document is longer than Teak keeps. */
368
+ truncated: boolean;
369
+ }
370
+ export type BulkCardsOperation = "create" | "update" | "favorite" | "delete" | "archive" | "unarchive" | "addTags" | "removeTags" | "restore";
166
371
  export interface BulkCardsResponse {
167
- operation: "create" | "update" | "favorite" | "delete";
372
+ /** The `cardId` of every item that failed, to retry just those. */
373
+ failedIds: string[];
374
+ operation: BulkCardsOperation;
168
375
  results: unknown[];
169
376
  summary: {
170
377
  failed: number;
@@ -172,6 +379,8 @@ export interface BulkCardsResponse {
172
379
  total: number;
173
380
  };
174
381
  }
382
+ /** Archived cards outside Trash: left out by default. */
383
+ export type ArchivedFilter = "exclude" | "only" | "include";
175
384
  export declare class TeakApiError extends Error {
176
385
  code: TeakApiErrorCode;
177
386
  requestId?: string;
@@ -186,6 +395,9 @@ export declare class TeakApiError extends Error {
186
395
  export declare const staticTokenProvider: (token: string) => TokenProvider;
187
396
  export declare const normalizeLimit: (limit?: number) => number;
188
397
  export declare const buildCardsSearchParams: (input: {
398
+ archived?: ArchivedFilter;
399
+ /** Only cards in this folder id. */
400
+ folder?: string;
189
401
  createdAfter?: number;
190
402
  createdBefore?: number;
191
403
  cursor?: string;
@@ -194,7 +406,20 @@ export declare const buildCardsSearchParams: (input: {
194
406
  hue?: string | readonly string[];
195
407
  include?: string;
196
408
  limit?: number;
409
+ /** How `query` finds cards; the API defaults to `hybrid`. */
410
+ mode?: SearchMode;
411
+ /**
412
+ * Search text. Accepts qualifiers: `tag:design` or `#design`, `type:link`,
413
+ * `is:fav`, `is:archived`, `site:github.com`, `folder:"Reading list"`,
414
+ * `price:<100` (also `>`, `<=`, `>=` and `=`), `text:"total due"` (only
415
+ * the words Teak read in images, video thumbnails, link previews and scanned
416
+ * PDFs) and `"quoted phrases"`.
417
+ */
197
418
  query?: string;
419
+ /** Required with `sort: "shuffle"`: the same seed gives the same order. */
420
+ shuffleSeed?: number;
421
+ /** A card id: only the cards closest to it in meaning. */
422
+ similarTo?: string;
198
423
  sort?: CardSort;
199
424
  style?: string | readonly string[];
200
425
  tag?: string;
@@ -209,13 +434,18 @@ export declare const createTeakClient: (options: {
209
434
  userAgent?: string;
210
435
  }) => {
211
436
  cards: {
212
- bulk: (operation: BulkCardsResponse["operation"], items: unknown[], requestOptions?: RequestOptions) => Promise<BulkCardsResponse>;
437
+ bulk: (operation: BulkCardsOperation, items: unknown[], requestOptions?: RequestOptions) => Promise<BulkCardsResponse>;
213
438
  changes: (input: {
214
439
  cursor?: string;
215
440
  limit?: number;
216
441
  since: number;
217
442
  }) => Promise<CardChangesResponse>;
218
443
  create: (input: CreateCardInput, requestOptions?: RequestOptions) => Promise<CreateCardResponse>;
444
+ /**
445
+ * The card's full stored text: document text, transcript, or a text
446
+ * card's content. Throws NO_CONTENT when the card has none.
447
+ */
448
+ content: (id: string) => Promise<CardContent>;
219
449
  /** Moves the card to Trash, or deletes it for good with `permanent`. */
220
450
  delete: (id: string, deleteOptions?: {
221
451
  permanent?: boolean;
@@ -224,9 +454,45 @@ export declare const createTeakClient: (options: {
224
454
  duplicate: (url: string) => Promise<string | null>;
225
455
  favorites: (input?: Parameters<(params: Parameters<typeof buildCardsSearchParams>[0]) => string>[0]) => Promise<CardsPage>;
226
456
  get: (id: string) => Promise<Card>;
457
+ /** Cards this card links to with `[[…]]`, and cards linking to it. */
458
+ links: (id: string) => Promise<CardLinks>;
227
459
  list: (input?: Parameters<(params: Parameters<typeof buildCardsSearchParams>[0]) => string>[0]) => Promise<CardsPage>;
460
+ /**
461
+ * A signed URL for a link card's new image; `setThumbnail` with bytes
462
+ * does this for you.
463
+ */
464
+ createThumbnailUpload: (id: string, input: CreateThumbnailUploadInput) => Promise<CreateUploadResponse>;
465
+ /** Refreshes summary, tags and link preview; it finishes in the background. */
466
+ refresh: (id: string) => Promise<RefreshCardResponse>;
467
+ /**
468
+ * Goes back to the image Teak found for a link card and deletes the one
469
+ * that replaced it. Changes nothing when the card has its own image.
470
+ */
471
+ resetThumbnail: (id: string) => Promise<Card>;
228
472
  restore: (id: string) => Promise<null>;
473
+ /** The cards closest in meaning to one card; never the card itself. */
474
+ similar: (id: string, input?: Parameters<(params: Parameters<typeof buildCardsSearchParams>[0]) => string>[0]) => Promise<CardsPage>;
229
475
  search: (input?: Parameters<(params: Parameters<typeof buildCardsSearchParams>[0]) => string>[0]) => Promise<CardsPage>;
476
+ /** Archives the card (hidden from the library, not deleted) or returns it. */
477
+ setArchived: (id: string, isArchived: boolean) => Promise<Card>;
478
+ /**
479
+ * Replaces a link card's image: uploads `bytes` (JPEG, PNG, WebP, GIF or
480
+ * AVIF, under 20 MB) and applies them. Pass `{ fileKey }` instead to
481
+ * apply an upload made with `createThumbnailUpload`.
482
+ */
483
+ setThumbnail: (id: string, image: BodyInit | {
484
+ fileKey: string;
485
+ }, mimeType?: string) => Promise<Card>;
486
+ /**
487
+ * Saves an arXiv link's paper as its own document card. Saving the same
488
+ * link again returns that card with `created: false`.
489
+ */
490
+ savePdf: (id: string) => Promise<SavedPdf>;
491
+ /**
492
+ * Marks a link card read, watched, bought or done (`done: true`), or
493
+ * not; repeating a call changes nothing. Only link cards can be marked.
494
+ */
495
+ setDone: (id: string, done: boolean) => Promise<Card>;
230
496
  setFavorite: (id: string, isFavorited: boolean) => Promise<Card>;
231
497
  update: (id: string, input: UpdateCardInput) => Promise<Card>;
232
498
  };
@@ -234,11 +500,60 @@ export declare const createTeakClient: (options: {
234
500
  latest: () => Promise<LatestExport>;
235
501
  start: () => Promise<ExportJob>;
236
502
  };
503
+ folders: {
504
+ /**
505
+ * Adds up to 100 cards; cards already in the folder are left as they
506
+ * are. A smart folder refuses cards (409 SMART_FOLDER).
507
+ */
508
+ addCards: (id: string, cardIds: string[]) => Promise<FolderCardsResult>;
509
+ /**
510
+ * Creates a folder; `{ kind: "smart", query }` saves a search, whose
511
+ * cards are whatever matches it when listed.
512
+ */
513
+ create: (input: {
514
+ color?: FolderColor;
515
+ kind?: "manual";
516
+ name: string;
517
+ } | {
518
+ color?: FolderColor;
519
+ kind: "smart";
520
+ name: string;
521
+ query: string;
522
+ }) => Promise<Folder>;
523
+ /** Deletes the folder; its cards are kept. */
524
+ delete: (id: string) => Promise<null>;
525
+ get: (id: string) => Promise<Folder>;
526
+ /** Every folder, sorted by name. */
527
+ list: () => Promise<{
528
+ items: Folder[];
529
+ }>;
530
+ removeCards: (id: string, cardIds: string[]) => Promise<FolderCardsResult>;
531
+ /**
532
+ * Renames or recolors; `color: null` clears the color. `query` changes
533
+ * a smart folder's search.
534
+ */
535
+ update: (id: string, input: {
536
+ color?: FolderColor | null;
537
+ name?: string;
538
+ query?: string;
539
+ }) => Promise<Folder>;
540
+ };
237
541
  me: () => Promise<Me>;
238
542
  tags: {
543
+ /**
544
+ * Deletes a tag from every card, including cards in Trash and Archive.
545
+ * It finishes in the background; poll with `operation`.
546
+ */
547
+ delete: (tag: string) => Promise<TagOperation>;
239
548
  list: () => Promise<{
240
549
  items: TagSummary[];
241
550
  }>;
551
+ /** Replaces `from` with `into` on every card; it finishes in the background. */
552
+ merge: (from: string, into: string) => Promise<TagOperation>;
553
+ /** A tag change's progress. */
554
+ operation: (id: string) => Promise<TagOperation>;
555
+ /** Renames a tag on every card; it finishes in the background. */
556
+ rename: (from: string, to: string) => Promise<TagOperation>;
242
557
  };
243
558
  uploads: {
244
559
  create: (input: CreateUploadInput) => Promise<CreateUploadResponse>;
@@ -1,4 +1,3 @@
1
- /** The shared cloud dev deployment every checkout and cloud session uses. */
2
1
  export declare const TEAK_DEV_DEPLOYMENT_NAME = "reminiscent-kangaroo-59";
3
2
  export declare const DEFAULT_TEAK_DEV_APP_URL = "http://localhost:3000";
4
3
  export declare const DEFAULT_TEAK_DEV_CONVEX_URL = "https://reminiscent-kangaroo-59.convex.cloud";
@@ -1,33 +1,30 @@
1
1
  export declare const SIGNUPS_PAUSED_MESSAGE = "New sign-ups are paused right now. Please try again later.";
2
- export declare const ACCOUNT_CHANGES_PAUSED_MESSAGE = "Account changes are paused right now. Please try again later.";
3
2
  export declare const cardTypes: readonly ["text", "link", "image", "video", "audio", "document", "palette", "quote"];
4
3
  export type CardType = (typeof cardTypes)[number];
5
- export declare const VISUAL_STYLE_TAXONOMY: readonly ["abstract", "cinematic", "dark", "illustrative", "minimal", "monochrome", "moody", "pastel", "photographic", "retro", "surreal", "vintage", "vibrant"];
4
+ export declare const VISUAL_STYLE_TAXONOMY: readonly ["abstract", "cinematic", "dark", "illustrative", "minimal", "monochrome", "moody", "pastel", "photographic", "retro", "surreal", "vintage", "vibrant", "typographic", "3d", "flat", "geometric", "hand-drawn", "futuristic"];
6
5
  export type VisualStyle = (typeof VISUAL_STYLE_TAXONOMY)[number];
7
6
  export declare const VISUAL_STYLE_LABELS: Record<VisualStyle, string>;
8
7
  export declare const VISUAL_STYLE_ALIAS_MAP: Record<string, VisualStyle>;
9
8
  export declare function normalizeVisualStyle(value: string): VisualStyle | null;
10
9
  export declare function normalizeVisualStyleFilters(values?: string[]): VisualStyle[];
11
10
  export declare function extractVisualStylesFromTags(tags?: string[]): VisualStyle[] | undefined;
11
+ export declare const COLOR_CARD_TYPES: readonly CardType[];
12
+ export declare const isColorCardType: (type: string) => boolean;
13
+ export declare const VISUAL_STYLE_CARD_TYPES: readonly CardType[];
14
+ export declare const isVisualStyleCardType: (type: string) => boolean;
15
+ export declare const isVisualStyle: (value: string) => value is VisualStyle;
12
16
  export declare const COLOR_HUE_BUCKETS: readonly ["red", "orange", "yellow", "green", "teal", "cyan", "blue", "purple", "pink", "brown", "neutral"];
13
17
  export type ColorHueBucket = (typeof COLOR_HUE_BUCKETS)[number];
14
18
  export declare const COLOR_HUE_LABELS: Record<ColorHueBucket, string>;
15
19
  export declare const COLOR_HUE_ALIAS_MAP: Record<string, ColorHueBucket>;
16
20
  export declare function normalizeColorHueBucket(value: string): ColorHueBucket | null;
17
21
  export declare function normalizeHueFilters(values?: string[]): ColorHueBucket[];
18
- /**
19
- * Maximum number of cards allowed for free tier users
20
- */
21
22
  export declare const FREE_TIER_LIMIT = 200;
22
- /**
23
- * Maximum file size for uploads (100MB in bytes)
24
- */
23
+ export declare const TRASH_RETENTION_MS: number;
25
24
  export { MAX_FILE_SIZE } from "./fileFormats.js";
26
- /**
27
- * Maximum number of files that can be uploaded at once
28
- */
29
- export declare const MAX_FILES_PER_UPLOAD = 5;
25
+ export declare const MAX_FILES_PER_UPLOAD = 20;
30
26
  export declare const MULTIPART_UPLOAD_THRESHOLD: number;
27
+ export declare const API_IDEMPOTENCY_KEY_TTL_MS: number;
31
28
  export declare const CARD_ERROR_CODES: {
32
29
  readonly CARD_LIMIT_REACHED: "CARD_LIMIT_REACHED";
33
30
  readonly CONTENT_TOO_LARGE: "CONTENT_TOO_LARGE";
@@ -40,45 +37,23 @@ export declare const CARD_ERROR_CODES: {
40
37
  };
41
38
  export type CardErrorCode = (typeof CARD_ERROR_CODES)[keyof typeof CARD_ERROR_CODES];
42
39
  export declare const CARD_ERROR_MESSAGES: Record<CardErrorCode, string>;
43
- /**
44
- * All card types as readonly array
45
- */
46
40
  export declare const CARD_TYPES: readonly CardType[];
47
- /**
48
- * Card type labels for UI display
49
- */
50
41
  export declare const CARD_TYPE_LABELS: Record<CardType, string>;
51
- /**
52
- * Card type icons mapping (for UI components)
53
- */
54
42
  export declare const CARD_TYPE_ICONS: Record<CardType, string>;
55
- /**
56
- * Card type configuration registry for centralized management
57
- */
58
43
  export interface CardTypeConfig {
59
44
  icon: string;
60
45
  label: string;
61
46
  searchLabel: string;
62
47
  }
63
48
  export declare const CARD_TYPE_REGISTRY: Record<CardType, CardTypeConfig>;
64
- /**
65
- * Helper functions for card type configuration
66
- */
67
49
  export declare function getCardTypeConfig(cardType: CardType): CardTypeConfig;
68
50
  export declare function getCardTypeIcon(cardType: CardType): string;
69
51
  export declare function getCardTypeLabel(cardType: CardType): string;
70
- /**
71
- * Typeahead option for search/filter dropdowns
72
- */
73
52
  export interface TypeaheadOption {
74
53
  label: string;
75
54
  value: CardType | "favorites" | "trash";
76
55
  }
77
- /**
78
- * Reserved keywords for search functionality
79
- */
80
56
  export declare const RESERVED_KEYWORDS: TypeaheadOption[];
81
- /**
82
- * Type guard to check if a value is a valid CardType
83
- */
84
57
  export declare function isCardType(value: unknown): value is CardType;
58
+ export { TEAK_FILES_ORIGINS } from "./filesOrigins.js";
59
+ export { HIDDEN_TAG_KEYS, isCardHidden, isHiddenTagList } from "./tags.js";
@@ -0,0 +1,4 @@
1
+ export declare const TEAK_FILES_ORIGINS: {
2
+ readonly production: "https://files.teakvault.com";
3
+ readonly development: "https://teak-files-dev.praveenjuge.workers.dev";
4
+ };
@@ -0,0 +1,27 @@
1
+ export declare const MAX_FOLDERS_PER_USER = 500;
2
+ export declare const MAX_FOLDERS_PER_CARD = 50;
3
+ export declare const MAX_FOLDER_NAME_LENGTH = 300;
4
+ export declare const MAX_FOLDER_CARD_IDS = 100;
5
+ export declare const MAX_SMART_FOLDER_QUERY_LENGTH = 500;
6
+ export type FolderKind = "manual" | "smart";
7
+ export declare const FOLDER_COLORS: readonly ["gray", "red", "orange", "amber", "green", "teal", "blue", "violet", "pink"];
8
+ export type FolderColor = (typeof FOLDER_COLORS)[number];
9
+ export declare const isFolderColor: (value: unknown) => value is FolderColor;
10
+ export declare const FOLDER_ERROR_MESSAGES: {
11
+ readonly DUPLICATE_NAME: "A folder with this name already exists";
12
+ readonly EMPTY_NAME: "Name the folder";
13
+ readonly NAME_TOO_LONG: "Folder names can be at most 300 characters";
14
+ readonly TOO_MANY_FOLDERS: "You can have up to 500 folders";
15
+ readonly TOO_MANY_FOLDERS_ON_CARD: "A card can be in up to 50 folders";
16
+ readonly NOT_FOUND: "Folder not found";
17
+ readonly EMPTY_QUERY: "Add a search for the smart folder";
18
+ readonly QUERY_TOO_LONG: "Smart folder searches can be at most 500 characters";
19
+ readonly SMART_FOLDER_IN_QUERY: "A smart folder's search can't use another smart folder";
20
+ readonly SMART_FOLDER_MEMBERSHIP: "Cards can't be added to a smart folder; it shows the cards that match its search";
21
+ readonly QUERY_ON_MANUAL_FOLDER: "Only smart folders have a search";
22
+ readonly SMART_FOLDER_TRASH: "Smart folders don't show Trash. To search for the word, put it in quotes";
23
+ };
24
+ export declare const normalizeFolderName: (name: string) => string;
25
+ export declare const folderNameKey: (name: string) => string;
26
+ export declare const explainInvalidFolderName: (name: string) => string | null;
27
+ export declare const explainInvalidSmartFolderQuery: (canonical: string) => string | null;
@@ -0,0 +1,2 @@
1
+ export declare const isLoopbackHostname: (hostname: string) => boolean;
2
+ export declare const validateOAuthUrl: (raw: unknown, local?: boolean) => URL;
@@ -0,0 +1,37 @@
1
+ export declare const MAX_TAG_NAME_LENGTH = 64;
2
+ export declare const TAG_OPERATION_COUNT_LIMIT = 10000;
3
+ export declare const TAG_OPERATION_STALL_MS: number;
4
+ export type TagOperationKind = "rename" | "merge" | "delete";
5
+ export type TagOperationStatus = "running" | "completed" | "failed";
6
+ export declare const TAG_ERROR_MESSAGES: {
7
+ readonly EMPTY_NAME: "Name the tag";
8
+ readonly NAME_TOO_LONG: "Tags can be at most 64 characters";
9
+ readonly NOT_FOUND: "Tag not found";
10
+ readonly SAME_TAG: "Choose a different tag to merge into";
11
+ readonly OPERATION_RUNNING: "Another tag change is still running";
12
+ readonly OPERATION_NOT_FOUND: "Tag operation not found";
13
+ };
14
+ export declare const tagKey: (tag: string) => string;
15
+ export declare const HIDDEN_TAG_KEYS: readonly ["secret", "hidden"];
16
+ export declare const isHiddenTagList: (tags: readonly string[] | null | undefined) => boolean;
17
+ export declare const isCardHidden: (card: {
18
+ isHidden?: true;
19
+ tags?: readonly string[];
20
+ }) => boolean;
21
+ export declare const explainInvalidTagName: (name: string) => string | null;
22
+ export declare const MAX_TAG_KEY_LENGTH = 256;
23
+ export declare const explainInvalidTagSource: (name: string) => string | null;
24
+ export declare const tagExistsMessage: (name: string) => string;
25
+ export type TagRewrite = {
26
+ kind: "delete";
27
+ from: string;
28
+ } | {
29
+ kind: "rename";
30
+ from: string;
31
+ to: string;
32
+ } | {
33
+ kind: "merge";
34
+ from: string;
35
+ to: string;
36
+ };
37
+ export declare const rewriteTagList: (tags: readonly string[] | undefined, rewrite: TagRewrite) => string[] | null;
@@ -0,0 +1,2 @@
1
+ export declare const codePointLength: (text: string) => number;
2
+ export declare const truncateCodePoints: (text: string, max: number) => string;
@@ -0,0 +1 @@
1
+ export declare const formatBytes: (bytes?: number | null) => string;
@@ -1,4 +1,7 @@
1
- export declare const WORKOS_RESOURCES: {
2
- readonly api: "https://teakvault.com/api";
3
- readonly mcp: "https://teakvault.com/mcp";
4
- };
1
+ export interface WorkosResources {
2
+ readonly api: string;
3
+ readonly mcp: string;
4
+ }
5
+ export declare const PRODUCTION_WORKOS_RESOURCE_ORIGIN = "https://teakvault.com";
6
+ export declare const workosResourcesFor: (url: string) => WorkosResources;
7
+ export declare const PRODUCTION_WORKOS_RESOURCES: WorkosResources;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teak-sdk",
3
- "version": "1.0.86",
3
+ "version": "1.0.87",
4
4
  "description": "Teak API client and OAuth discovery for browser and Node applications.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -27,7 +27,7 @@
27
27
  "devDependencies": {
28
28
  "@teak/convex": "workspace:*",
29
29
  "@types/bun": "^1.4.0",
30
- "@types/node": "^26.2.0",
30
+ "@types/node": "^26.6.5",
31
31
  "typescript": "^6.0.3"
32
32
  },
33
33
  "publishConfig": {