@oddington/sdk 0.1.24 → 0.1.25

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/browser.js CHANGED
@@ -413,7 +413,14 @@ function parseFileDescriptor(value) {
413
413
  if (Object.keys(f).sort().join(",") !== "details,enrichments,id,mediaType,name,path,sha256,size" || typeof f.id != "string" || !FILE_ID_PATTERN.test(f.id) || !safeFileName(f.name) || !safeFilePath(f.path) || !Number.isSafeInteger(f.size) || f.size < 0 || f.size > FILE_ORIGINAL_MAX_BYTES || typeof f.sha256 != "string" || !/^[0-9a-f]{64}$/.test(f.sha256) || !f.enrichments || typeof f.enrichments != "object" || Array.isArray(f.enrichments) || !f.details || typeof f.details != "object" || Array.isArray(f.details)) return invalid8();
414
414
  let d = f.details;
415
415
  if (d.kind === "other") {
416
- if (Object.keys(d).length !== 1 || f.mediaType !== "application/octet-stream") return invalid8();
416
+ if (Object.keys(d).length !== 1 || ![
417
+ "application/octet-stream",
418
+ "text/plain",
419
+ "text/markdown",
420
+ "application/json",
421
+ "image/png",
422
+ "image/jpeg"
423
+ ].includes(f.mediaType)) return invalid8();
417
424
  } else if (d.kind === "csv") {
418
425
  if (Object.keys(d).sort().join(",") !== "dataRows,delimiter,encoding,headerRow,kind" || f.mediaType !== "text/csv" || !["utf-8", "windows-1252"].includes(d.encoding) || ![",", ";", " "].includes(d.delimiter) || !Number.isSafeInteger(d.headerRow) || d.headerRow < 1 || !Number.isSafeInteger(d.dataRows) || d.dataRows < 0) return invalid8();
419
426
  } else if (d.kind === "pdf") {
@@ -452,6 +459,11 @@ function safeFileName(value) {
452
459
  function safeFilePath(value) {
453
460
  return typeof value == "string" && value.length <= 1024 && value.split("/").every(safeFileName);
454
461
  }
462
+ var ORIGINAL_MEDIA_TYPES = ["application/pdf", "text/plain", "text/markdown", "application/json", "image/png", "image/jpeg"];
463
+ function declaredOriginalMediaType(value) {
464
+ let normalized = value.split(";", 1)[0].trim().toLowerCase();
465
+ return ORIGINAL_MEDIA_TYPES.includes(normalized) ? normalized : void 0;
466
+ }
455
467
  function parseFileCreateMetadata(value, maxBytes) {
456
468
  let invalid8 = () => {
457
469
  throw new FileOperationError("invalid_input", 400);
@@ -460,7 +472,7 @@ function parseFileCreateMetadata(value, maxBytes) {
460
472
  let input = value;
461
473
  if (Object.keys(input).some((key) => !["name", "path", "storage", "csv", "inputMediaType", "size", "requestKey"].includes(key))) return invalid8();
462
474
  let path = input.path === void 0 ? "Files" : input.path;
463
- if (!safeFileName(input.name) || !safeFilePath(path) || path.split("/")[0].toLowerCase() === ".os" || input.storage !== void 0 && input.storage !== "remote" && input.storage !== "local" || input.inputMediaType !== void 0 && input.inputMediaType !== "application/pdf" || !Number.isSafeInteger(input.size) || input.size < 0) return invalid8();
475
+ if (!safeFileName(input.name) || !safeFilePath(path) || path.split("/")[0].toLowerCase() === ".os" || input.storage !== void 0 && input.storage !== "remote" && input.storage !== "local" || input.inputMediaType !== void 0 && (typeof input.inputMediaType != "string" || !ORIGINAL_MEDIA_TYPES.includes(input.inputMediaType)) || !Number.isSafeInteger(input.size) || input.size < 0) return invalid8();
464
476
  if (input.size > maxBytes) throw new FileOperationError("request_too_large", 413);
465
477
  if (typeof input.requestKey != "string" || input.requestKey.length < 1 || input.requestKey.length > 256 || input.requestKey !== input.requestKey.trim() || /[\u0000-\u001f\u007f]/.test(input.requestKey)) return invalid8();
466
478
  let csv;
@@ -476,7 +488,7 @@ function parseFileCreateMetadata(value, maxBytes) {
476
488
  name: input.name,
477
489
  path,
478
490
  ...input.storage ? { storage: input.storage } : {},
479
- ...input.inputMediaType === "application/pdf" ? { inputMediaType: input.inputMediaType } : {},
491
+ ...input.inputMediaType === void 0 ? {} : { inputMediaType: input.inputMediaType },
480
492
  ...csv ? { csv } : {},
481
493
  size: input.size,
482
494
  requestKey: input.requestKey
@@ -590,14 +602,16 @@ function validateRoutineSchemaValue(schema2, value) {
590
602
  var ROUTINE_DECLARATION_SCHEMA = "https://schemas.oddington.ai/routine/v1.json", ROUTINE_SOURCE_LIMITS = { files: 2e4, bytes: 256 * 1024 * 1024, declarationBytes: 64 * 1024 };
591
603
  var invalid = (field) => {
592
604
  throw new Error(`Invalid routine declaration: ${field}`);
593
- }, object2 = (value, fields5, field) => ((!value || typeof value != "object" || Array.isArray(value) || Object.keys(value).some((key) => !fields5.includes(key))) && invalid(field), value), routineAlias = (value) => typeof value == "string" && /^[a-z][a-z0-9-]{0,62}$/.test(value), list = (value, field, allowed, qualified = !1) => value === void 0 ? [] : ((!Array.isArray(value) || value.length > 64 || value.some((item) => !(routineAlias(item) || qualified && typeof item == "string" && item.split("/").length === 2 && item.split("/").every(routineAlias)) || allowed && !allowed.includes(item)) || new Set(value).size !== value.length) && invalid(field), [...value].sort());
605
+ }, object2 = (value, fields5, field) => ((!value || typeof value != "object" || Array.isArray(value) || Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null || Reflect.ownKeys(value).some((key) => typeof key != "string" || !fields5.includes(key) || !Object.getOwnPropertyDescriptor(value, key).enumerable || !Object.hasOwn(Object.getOwnPropertyDescriptor(value, key), "value"))) && invalid(field), value), routineAlias = (value) => typeof value == "string" && /^[a-z][a-z0-9-]{0,62}$/.test(value), list = (value, field, allowed, qualified = !1) => value === void 0 ? [] : ((!Array.isArray(value) || value.length > 64 || value.some((item) => !(routineAlias(item) || qualified && typeof item == "string" && item.split("/").length === 2 && item.split("/").every(routineAlias)) || allowed && !allowed.includes(item)) || new Set(value).size !== value.length) && invalid(field), [...value].sort());
594
606
  function routineAssetPattern(value) {
595
607
  return typeof value == "string" && value.length <= 512 && !value.startsWith("/") && !value.includes("\\") && !value.includes(":") && value.split("/").every((part) => !!part && part !== "." && part !== ".." && /^[a-zA-Z0-9_.*-]+$/.test(part));
596
608
  }
597
609
  function parseRoutineDeclaration(value) {
598
610
  let v = object2(value, ["$schema", "access", "runtime", "retry", "assets"], "fields");
599
611
  v.$schema !== ROUTINE_DECLARATION_SCHEMA && invalid("$schema");
600
- let access = object2(v.access === void 0 ? {} : v.access, ["repositories", "connections", "models", "files", "publish"], "access"), rawRepositories = access.repositories === void 0 ? {} : access.repositories, repositories = object2(rawRepositories, rawRepositories && typeof rawRepositories == "object" ? Object.keys(rawRepositories) : [], "access.repositories");
612
+ let access = object2(v.access === void 0 ? {} : v.access, ["repositories", "connections", "ai", "files", "publish"], "access");
613
+ Object.hasOwn(access, "ai") && access.ai !== !0 && invalid("access.ai");
614
+ let rawRepositories = access.repositories === void 0 ? {} : access.repositories, repositories = object2(rawRepositories, rawRepositories && typeof rawRepositories == "object" ? Object.keys(rawRepositories) : [], "access.repositories");
601
615
  Object.keys(repositories).length > 64 && invalid("access.repositories");
602
616
  let grants = {};
603
617
  for (let key of Object.keys(repositories).sort())
@@ -615,7 +629,7 @@ function parseRoutineDeclaration(value) {
615
629
  access: {
616
630
  repositories: grants,
617
631
  connections: list(access.connections, "access.connections", void 0, !0),
618
- models: list(access.models, "access.models"),
632
+ ...access.ai === !0 ? { ai: !0 } : {},
619
633
  ...access.files === void 0 ? {} : { files: access.files },
620
634
  publish: list(access.publish, "access.publish", ["asks", "digests"])
621
635
  },
@@ -663,7 +677,7 @@ function assertRoutineFields(value, allowed) {
663
677
  }
664
678
  function parseRoutineAccess(value) {
665
679
  try {
666
- value !== void 0 && assertRoutineJson(value), assertRoutineFields(value ?? {}, ["repositories", "connections", "models", "files", "publish"]);
680
+ value !== void 0 && assertRoutineJson(value), assertRoutineFields(value ?? {}, ["repositories", "connections", "ai", "files", "publish"]);
667
681
  let access = value, names = access?.connections ?? [];
668
682
  if (!Array.isArray(names) || names.length > 64 || new Set(names).size !== names.length || names.some((name) => typeof name != "string" || name.split("/").length !== 2 || name.split("/").some((part) => !isAppRoutineName(part)))) return invalidContract2();
669
683
  let parsed = parseRoutineDeclaration({ $schema: ROUTINE_DECLARATION_SCHEMA, access: { ...access, connections: [] } }).access;
@@ -673,6 +687,52 @@ function parseRoutineAccess(value) {
673
687
  }
674
688
  }
675
689
 
690
+ var ROUTINE_AI_VALUE_BYTES = 256 * 1024, ROUTINE_AI_EVIDENCE_BYTES = 4 * 1024;
691
+ var RoutineAIError = class extends Error {
692
+ constructor(code) {
693
+ super(code);
694
+ this.code = code;
695
+ this.name = "RoutineAIError";
696
+ }
697
+ code;
698
+ };
699
+ function capture(value, invalid8, oversized) {
700
+ try {
701
+ assertRoutineJson(value);
702
+ let encoded = JSON.stringify(value);
703
+ if (new TextEncoder().encode(encoded).byteLength > ROUTINE_AI_VALUE_BYTES) throw new RoutineAIError(oversized);
704
+ return freezeRoutineMetadata(JSON.parse(encoded));
705
+ } catch (error) {
706
+ throw error instanceof RoutineAIError ? error : new RoutineAIError(invalid8);
707
+ }
708
+ }
709
+ function parseRoutineAIRequest(value) {
710
+ let v = capture(value, "invalid_input", "input_too_large");
711
+ try {
712
+ if (assertRoutineFields(v, v.kind === "generateObject" ? ["kind", "instructions", "input", "schema"] : ["kind", "instructions", "input"]), !["generateText", "generateObject"].includes(v.kind) || typeof v.instructions != "string" || !v.instructions.trim()) throw new Error();
713
+ let input = typeof v.input == "string" ? v.input : parseFileDescriptor(v.input);
714
+ if (v.kind === "generateText") return freezeRoutineMetadata({ kind: v.kind, instructions: v.instructions, input });
715
+ let schema2 = parseAppRoutineContract({ routine: "ai", input: v.schema, steps: [{ id: "generate" }] }).input;
716
+ return freezeRoutineMetadata({ kind: v.kind, instructions: v.instructions, input, schema: schema2 });
717
+ } catch {
718
+ throw new RoutineAIError("invalid_input");
719
+ }
720
+ }
721
+ function parseRoutineAIResult(request2, value) {
722
+ let v = capture(value, "invalid_output", "output_too_large");
723
+ try {
724
+ if (request2.kind === "generateText") {
725
+ if (assertRoutineFields(v, ["text"]), typeof v.text != "string" || !v.text.trim()) throw new Error();
726
+ return freezeRoutineMetadata({ text: v.text });
727
+ } else {
728
+ if (assertRoutineFields(v, ["object"]), !Object.hasOwn(v, "object")) throw new Error();
729
+ return validateRoutineSchemaValue(request2.schema, v.object), freezeRoutineMetadata({ object: v.object });
730
+ }
731
+ } catch {
732
+ throw new RoutineAIError("invalid_output");
733
+ }
734
+ }
735
+
676
736
  var LIMIT = 256 * 1024, invalid3 = () => {
677
737
  throw new AppRoutineContractError("invalid_value");
678
738
  }, invalidContract3 = () => {
@@ -683,7 +743,7 @@ function captureJson(value) {
683
743
  let encoded = JSON.stringify(value);
684
744
  return new TextEncoder().encode(encoded).byteLength > LIMIT ? invalid3() : freezeRoutineMetadata(JSON.parse(encoded));
685
745
  }
686
- function capture(schema2, value) {
746
+ function capture2(schema2, value) {
687
747
  let captured = captureJson(value);
688
748
  return validateRoutineSchemaValue(schema2, captured), captured;
689
749
  }
@@ -709,7 +769,7 @@ function declaration(value, defaults) {
709
769
  } catch {
710
770
  return invalidContract3();
711
771
  }
712
- let parsed = freezeRoutineMetadata(parseAppRoutineContract({ routine: "definition", input: value, steps: [{ id: "inspect" }] }).input), captured = defaults ? capture(parsed, defaults.value) : void 0, builder = Object.defineProperty({ ...parsed }, "default", { value: (value2) => declaration(parsed, { value: value2 }) });
772
+ let parsed = freezeRoutineMetadata(parseAppRoutineContract({ routine: "definition", input: value, steps: [{ id: "inspect" }] }).input), captured = defaults ? capture2(parsed, defaults.value) : void 0, builder = Object.defineProperty({ ...parsed }, "default", { value: (value2) => declaration(parsed, { value: value2 }) });
713
773
  return schemas.set(builder, { schema: parsed, ...defaults ? { default: captured } : {} }), Object.freeze(builder);
714
774
  }
715
775
  var schema = {
@@ -794,10 +854,32 @@ function inspectRoutineDefinition(definition) {
794
854
  }
795
855
  function captureRoutineInput(definition, input) {
796
856
  let contract = inspectRoutineDefinition(definition);
797
- return capture(contract.input, input === void 0 && Object.hasOwn(contract, "inputDefault") ? contract.inputDefault : input);
857
+ return capture2(contract.input, input === void 0 && Object.hasOwn(contract, "inputDefault") ? contract.inputDefault : input);
798
858
  }
799
859
  async function executeRoutineDefinition(definition, input, runtime) {
800
- let captured = captureRoutineInput(definition, input), { contract, steps, run } = definitions.get(definition), active2 = !0, accepting = !0, broken = !1, work = Promise.resolve(), called = /* @__PURE__ */ new Set(), reached = /* @__PURE__ */ new Set(), guarded = (value, receiver) => typeof value == "function" ? (...args) => {
860
+ let captured = captureRoutineInput(definition, input), { contract, steps, run } = definitions.get(definition), active2 = !0, accepting = !0, broken = !1, work = Promise.resolve(), aiFailure, aiWork = /* @__PURE__ */ new Set(), generate = (kind, value) => {
861
+ let operation2 = (async () => {
862
+ if (!active2 || broken || contract.access.ai !== !0) throw new RoutineAIError("access_denied");
863
+ if (!runtime.ai) throw new RoutineAIError("capability_unavailable");
864
+ let request2;
865
+ try {
866
+ assertRoutineFields(value, kind === "generateObject" ? ["instructions", "input", "schema"] : ["instructions", "input"]), request2 = parseRoutineAIRequest({
867
+ kind,
868
+ instructions: value.instructions,
869
+ input: value.input,
870
+ ...kind === "generateObject" ? { schema: schemaMetadata(value.schema).schema } : {}
871
+ });
872
+ } catch (error) {
873
+ throw error instanceof RoutineAIError ? error : new RoutineAIError("invalid_input");
874
+ }
875
+ let result3 = await runtime.ai[kind](request2);
876
+ if (!active2 || broken) throw new RoutineAIError("access_denied");
877
+ return parseRoutineAIResult(request2, result3);
878
+ })();
879
+ return aiWork.add(operation2), operation2.then(() => aiWork.delete(operation2), (error) => {
880
+ aiFailure ??= error, broken = !0, aiWork.delete(operation2);
881
+ }), operation2;
882
+ }, called = /* @__PURE__ */ new Set(), reached = /* @__PURE__ */ new Set(), guarded = (value, receiver) => typeof value == "function" ? (...args) => {
801
883
  if (!active2 || broken) throw new Error("Run authority ended");
802
884
  return guarded(Reflect.apply(value, receiver, args));
803
885
  } : value && typeof value == "object" && !(value instanceof Promise) ? new Proxy({ ...value }, {
@@ -810,6 +892,10 @@ async function executeRoutineDefinition(definition, input, runtime) {
810
892
  ...guarded({
811
893
  ...runtime,
812
894
  run: freezeRoutineMetadata({ ...runtime.run }),
895
+ ai: {
896
+ generateText: (value) => generate("generateText", value),
897
+ generateObject: (value) => generate("generateObject", value)
898
+ },
813
899
  connections: { ...runtime.connections, get: async (identity) => {
814
900
  if (!active2 || broken || !contract.access.connections?.includes(identity)) throw new Error("Connection unavailable");
815
901
  let value = await runtime.connections.get(identity);
@@ -850,10 +936,11 @@ async function executeRoutineDefinition(definition, input, runtime) {
850
936
  } catch (error) {
851
937
  failure = error, broken = !0;
852
938
  }
853
- accepting = !1, await work, active2 = !1;
939
+ for (accepting = !1, await work; aiWork.size; ) await Promise.allSettled([...aiWork]);
940
+ active2 = !1;
854
941
  for (let step of contract.steps) reached.has(step.id) || await runtime.step(step.id, { status: "skipped" });
855
- if (broken) throw failure ?? new AppRoutineContractError("invalid_value");
856
- if (contract.result !== void 0) return capture(contract.result, result2);
942
+ if (broken) throw failure ?? aiFailure ?? new AppRoutineContractError("invalid_value");
943
+ if (contract.result !== void 0) return capture2(contract.result, result2);
857
944
  if (result2 !== void 0) return invalid3();
858
945
  }
859
946
 
@@ -1133,19 +1220,19 @@ var invalid6 = () => {
1133
1220
  }, unavailable = () => {
1134
1221
  throw new DataOperationError("unavailable");
1135
1222
  }, text2 = (value) => typeof value == "string" && value.length > 0;
1136
- function capture2(value, ancestors = /* @__PURE__ */ new Set(), depth = 0) {
1223
+ function capture3(value, ancestors = /* @__PURE__ */ new Set(), depth = 0) {
1137
1224
  if (value === null || typeof value == "string" || typeof value == "boolean" || typeof value == "number" && Number.isFinite(value)) return value;
1138
1225
  if (!value || typeof value != "object" || ancestors.has(value) || depth > 32) return invalid6();
1139
1226
  ancestors.add(value);
1140
1227
  try {
1141
- return Array.isArray(value) ? historyArray(value).map((item) => capture2(item, ancestors, depth + 1)) : Object.fromEntries(Object.entries(historyProperties(value)).map(([key, descriptor]) => [key, capture2(descriptor.value, ancestors, depth + 1)]));
1228
+ return Array.isArray(value) ? historyArray(value).map((item) => capture3(item, ancestors, depth + 1)) : Object.fromEntries(Object.entries(historyProperties(value)).map(([key, descriptor]) => [key, capture3(descriptor.value, ancestors, depth + 1)]));
1142
1229
  } finally {
1143
1230
  ancestors.delete(value);
1144
1231
  }
1145
1232
  }
1146
1233
  function recordPagination(select) {
1147
1234
  function pages(query) {
1148
- let captured = capture2(query), names = Object.keys(historyProperties(captured));
1235
+ let captured = capture3(query), names = Object.keys(historyProperties(captured));
1149
1236
  for (let name of names) {
1150
1237
  let selection2 = historyProperties(captured[name]);
1151
1238
  if (Object.hasOwn(selection2, "$")) {
@@ -1167,7 +1254,7 @@ function recordPagination(select) {
1167
1254
  return [name, next === void 0 ? selection2 : { ...selection2, $: { ...selection2.$, after: next } }];
1168
1255
  })), response2 = await select(request2), page;
1169
1256
  try {
1170
- page = capture2(response2);
1257
+ page = capture3(response2);
1171
1258
  let keys4 = Object.keys(historyProperties(page));
1172
1259
  (keys4.length !== active2.length + 1 || keys4.some((key) => key !== "$meta" && !active2.includes(key))) && unavailable();
1173
1260
  let meta = page.$meta;
@@ -1645,13 +1732,13 @@ var files = {
1645
1732
  if (intrinsic !== void 0 && input.size !== void 0 && input.size !== intrinsic) throw invalid7();
1646
1733
  let name = input.name;
1647
1734
  name === void 0 && blob && "name" in input.file && typeof input.file.name == "string" && (name = typeof globalThis.File < "u" && input.file instanceof globalThis.File ? input.file.name : input.file.name.split(/[\\/]/).at(-1));
1648
- let metadata = parseFileCreateMetadata({
1735
+ let inputMediaType = blob ? declaredOriginalMediaType(input.file.type) : void 0, metadata = parseFileCreateMetadata({
1649
1736
  name,
1650
1737
  size: intrinsic ?? input.size,
1651
1738
  path: input.path,
1652
1739
  storage: input.storage,
1653
1740
  csv: input.csv,
1654
- ...blob && input.file.type === "application/pdf" ? { inputMediaType: "application/pdf" } : {},
1741
+ ...inputMediaType === void 0 ? {} : { inputMediaType },
1655
1742
  requestKey: options?.requestKey === void 0 ? routineTransport()?.key("files") ?? crypto.randomUUID() : options.requestKey
1656
1743
  }, FILE_ORIGINAL_MAX_BYTES);
1657
1744
  if (!context2.local && !context2.localFiles && metadata.storage === "local") throw new FileOperationError("unsupported_context", 400);
@@ -1733,29 +1820,58 @@ var files = {
1733
1820
  read
1734
1821
  };
1735
1822
 
1823
+ function validDropboxRootPath(value) {
1824
+ return typeof value == "string" && value.length <= 2048 && value.startsWith("/") && !/[\\\x00-\x1f\x7f]/.test(value) && value.slice(1).split("/").every((segment) => segment.length > 0 && segment.trim() === segment && segment !== "." && segment !== "..");
1825
+ }
1736
1826
  function validGoogleScopes(value) {
1737
1827
  return Array.isArray(value) && value.length > 0 && value.length <= 32 && value.every((scope) => typeof scope == "string" && scope.length <= 256 && /^(https:\/\/www\.googleapis\.com\/auth\/[A-Za-z0-9._-]+|https:\/\/mail\.google\.com\/|openid|email|profile)$/.test(scope)) && new Set(value).size === value.length;
1738
1828
  }
1829
+ var DROPBOX_FILE_SCOPES = ["account_info.read", "files.metadata.read", "files.content.read", "files.content.write"];
1830
+ function validDropboxFileScopes(value) {
1831
+ return Array.isArray(value) && value.length === DROPBOX_FILE_SCOPES.length && new Set(value).size === value.length && DROPBOX_FILE_SCOPES.every((scope) => value.includes(scope));
1832
+ }
1739
1833
 
1740
1834
  var ROUTINE_CONNECTION_CREDENTIALS_ENV = "OS_ROUTINE_CONNECTION_CREDENTIALS";
1835
+ function parseRoutineConnectionNames(value) {
1836
+ if (!Array.isArray(value) || value.some((name) => typeof name != "string" || !/^(gmail|airtable|dropbox)\/[a-z0-9][a-z0-9-]{0,62}$/.test(name)) || new Set(value).size !== value.length)
1837
+ throw new Error("Invalid routine connections");
1838
+ return value;
1839
+ }
1741
1840
  var connectionUuid = (v) => typeof v == "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(v);
1742
1841
  function runtimeConnection(lease) {
1743
- return lease.provider === "gmail" ? {
1744
- id: `gmail/${lease.name}`,
1745
- provider: lease.provider,
1746
- name: lease.name,
1747
- accountEmail: lease.accountEmail,
1748
- scopes: [...lease.scopes],
1749
- accessToken: lease.accessToken,
1750
- expiresAt: lease.expiresAt
1751
- } : {
1752
- id: `airtable/${lease.name}`,
1753
- provider: lease.provider,
1754
- name: lease.name,
1755
- baseId: lease.baseId,
1756
- accessToken: lease.accessToken,
1757
- runExpiresAt: lease.runExpiresAt
1758
- };
1842
+ switch (lease.provider) {
1843
+ case "gmail":
1844
+ return {
1845
+ id: `gmail/${lease.name}`,
1846
+ provider: lease.provider,
1847
+ name: lease.name,
1848
+ accountEmail: lease.accountEmail,
1849
+ scopes: [...lease.scopes],
1850
+ accessToken: lease.accessToken,
1851
+ expiresAt: lease.expiresAt
1852
+ };
1853
+ case "airtable":
1854
+ return {
1855
+ id: `airtable/${lease.name}`,
1856
+ provider: lease.provider,
1857
+ name: lease.name,
1858
+ baseId: lease.baseId,
1859
+ accessToken: lease.accessToken,
1860
+ runExpiresAt: lease.runExpiresAt
1861
+ };
1862
+ case "dropbox":
1863
+ return {
1864
+ id: `dropbox/${lease.name}`,
1865
+ provider: lease.provider,
1866
+ name: lease.name,
1867
+ accountId: lease.accountId,
1868
+ access: lease.access,
1869
+ rootPath: lease.rootPath,
1870
+ scopes: [...lease.scopes],
1871
+ accessToken: lease.accessToken,
1872
+ expiresAt: lease.expiresAt
1873
+ };
1874
+ }
1759
1875
  }
1760
1876
  function parseConnectionLease(raw, now = Date.now()) {
1761
1877
  let v = raw;
@@ -1766,7 +1882,7 @@ function parseConnectionLease(raw, now = Date.now()) {
1766
1882
  throw new Error("Invalid connection credential");
1767
1883
  return v;
1768
1884
  }
1769
- if (v.provider === "airtable" && Object.keys(v).sort().join(",") === "accessToken,baseId,connectionId,name,provider,runExpiresAt,runId" && typeof v.baseId == "string" && /^app[A-Za-z0-9]+$/.test(v.baseId) && typeof v.runExpiresAt == "string" && Number.isFinite(Date.parse(v.runExpiresAt)) && Date.parse(v.runExpiresAt) > now + 6e4 && Date.parse(v.runExpiresAt) <= now + 15 * 6e4)
1885
+ if (v.provider === "airtable" && Object.keys(v).sort().join(",") === "accessToken,baseId,connectionId,name,provider,runExpiresAt,runId" && typeof v.baseId == "string" && /^app[A-Za-z0-9]+$/.test(v.baseId) && typeof v.runExpiresAt == "string" && Number.isFinite(Date.parse(v.runExpiresAt)) && Date.parse(v.runExpiresAt) > now + 6e4 && Date.parse(v.runExpiresAt) <= now + 15 * 6e4 || v.provider === "dropbox" && Object.keys(v).sort().join(",") === "access,accessToken,accountId,connectionId,expiresAt,name,provider,rootPath,runId,scopes" && typeof v.accountId == "string" && /^dbid:[A-Za-z0-9_-]{1,128}$/.test(v.accountId) && v.access === "full_dropbox" && validDropboxRootPath(v.rootPath) && validDropboxFileScopes(v.scopes) && typeof v.expiresAt == "string" && Number.isFinite(Date.parse(v.expiresAt)) && Date.parse(v.expiresAt) > now + 6e4 && Date.parse(v.expiresAt) <= now + 144e5)
1770
1886
  return v;
1771
1887
  throw new Error("Invalid connection credential");
1772
1888
  }
@@ -2305,9 +2421,9 @@ function parseAnswerableQuestion(value) {
2305
2421
  let entry = object4(json(value));
2306
2422
  keys3(
2307
2423
  entry,
2308
- ["itemId", "revision", "title", "inboxUrl", "questionId", "routine", "prompt", "response", "answerable", "tags", "recordRefs", "instructions"],
2309
- ["itemId", "revision", "title", "inboxUrl", "questionId", "routine", "prompt", "response", "answerable"]
2310
- ), assert(id2(entry.itemId) && integer(entry.revision) && text4(entry.title) && text4(entry.inboxUrl));
2424
+ ["itemId", "number", "revision", "title", "inboxUrl", "questionId", "routine", "prompt", "response", "answerable", "tags", "recordRefs", "instructions"],
2425
+ ["itemId", "number", "revision", "title", "inboxUrl", "questionId", "routine", "prompt", "response", "answerable"]
2426
+ ), assert(id2(entry.itemId) && integer(entry.number) && integer(entry.revision) && text4(entry.title) && text4(entry.inboxUrl));
2311
2427
  let link = new URL(entry.inboxUrl), slug = link.hostname.slice(0, -13);
2312
2428
  return assert(link.hostname.endsWith(".oddington.ai") && slug.length <= 63 && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(slug) && !["auth", "www", "api", "os"].includes(slug) && entry.inboxUrl === `https://${slug}.oddington.ai/inbox/${entry.itemId}`), assert(isAppRoutineName(entry.routine) && text4(entry.questionId) && entry.questionId === entry.questionId.trim() && text4(entry.prompt) && (!("instructions" in entry) || text4(entry.instructions, !0)) && typeof entry.answerable == "boolean"), responseDefinition(entry.response), askMetadata(entry), entry;
2313
2429
  }