@oddington/sdk 0.1.24 → 0.1.26
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/README.md +5 -3
- package/dist/browser.js +171 -46
- package/dist/index.js +2 -2
- package/dist/types/packages/sdk/src/public.d.ts +2 -2
- package/dist/types/packages/sdk/src/routine-definition-types.d.ts +18 -0
- package/dist/types/shared/app-routine-contract.d.ts +1 -0
- package/dist/types/shared/connection-runtime.d.ts +13 -2
- package/dist/types/shared/files-contract.d.ts +12 -2
- package/dist/types/shared/inbox-member-types.d.ts +2 -0
- package/dist/types/shared/routine-ai-contract.d.ts +40 -0
- package/dist/types/shared/routine-definition-contract.d.ts +1 -1
- package/dist/types/shared/routine-deployment-contract.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ import { defineRoutine, file, schema } from '@oddington/sdk';
|
|
|
40
40
|
|
|
41
41
|
export default defineRoutine({
|
|
42
42
|
id: 'invoice-import',
|
|
43
|
-
input: schema.object({ document: file() }),
|
|
43
|
+
input: schema.object({ document: file({ storage: 'local', path: 'Statements' }) }),
|
|
44
44
|
access: { files: 'read' },
|
|
45
45
|
steps: {
|
|
46
46
|
read: {
|
|
@@ -67,8 +67,10 @@ skipped. Omitted input normalizes to `{}`. A top-level schema can supply a valid
|
|
|
67
67
|
immutable default with `.default(value)`, such as
|
|
68
68
|
`schema.object({ files: schema.array(file(), { maxItems: 32 }) }).default({ files: [] })`.
|
|
69
69
|
Nested defaults are unsupported and rejected. Omitted steps permit no step call;
|
|
70
|
-
omitted result permits no returned value. `file()` declares a complete OS File descriptor
|
|
71
|
-
|
|
70
|
+
omitted result permits no returned value. `file()` declares a complete OS File descriptor.
|
|
71
|
+
Its optional `storage` selects local or remote placement and its optional `path`
|
|
72
|
+
selects a managed OS parent such as `Statements`; it is never a machine or provider
|
|
73
|
+
path. The trusted launcher separately verifies current File-use authority. The supplied
|
|
72
74
|
runtime contains `run.id`, `data`, `files`, `connections`, `inbox`, and `step`.
|
|
73
75
|
Accepted input and saved values are immutable snapshots. Access declarations set
|
|
74
76
|
maximum named capabilities and grant no authority; the trusted host checks current
|
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 ||
|
|
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,14 @@ function safeFileName(value) {
|
|
|
452
459
|
function safeFilePath(value) {
|
|
453
460
|
return typeof value == "string" && value.length <= 1024 && value.split("/").every(safeFileName);
|
|
454
461
|
}
|
|
462
|
+
function safeManagedFilePath(value) {
|
|
463
|
+
return safeFilePath(value) && value.split("/")[0].toLowerCase() !== ".os";
|
|
464
|
+
}
|
|
465
|
+
var ORIGINAL_MEDIA_TYPES = ["application/pdf", "text/plain", "text/markdown", "application/json", "image/png", "image/jpeg"];
|
|
466
|
+
function declaredOriginalMediaType(value) {
|
|
467
|
+
let normalized = value.split(";", 1)[0].trim().toLowerCase();
|
|
468
|
+
return ORIGINAL_MEDIA_TYPES.includes(normalized) ? normalized : void 0;
|
|
469
|
+
}
|
|
455
470
|
function parseFileCreateMetadata(value, maxBytes) {
|
|
456
471
|
let invalid8 = () => {
|
|
457
472
|
throw new FileOperationError("invalid_input", 400);
|
|
@@ -460,7 +475,7 @@ function parseFileCreateMetadata(value, maxBytes) {
|
|
|
460
475
|
let input = value;
|
|
461
476
|
if (Object.keys(input).some((key) => !["name", "path", "storage", "csv", "inputMediaType", "size", "requestKey"].includes(key))) return invalid8();
|
|
462
477
|
let path = input.path === void 0 ? "Files" : input.path;
|
|
463
|
-
if (!safeFileName(input.name) || !
|
|
478
|
+
if (!safeFileName(input.name) || !safeManagedFilePath(path) || 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
479
|
if (input.size > maxBytes) throw new FileOperationError("request_too_large", 413);
|
|
465
480
|
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
481
|
let csv;
|
|
@@ -476,7 +491,7 @@ function parseFileCreateMetadata(value, maxBytes) {
|
|
|
476
491
|
name: input.name,
|
|
477
492
|
path,
|
|
478
493
|
...input.storage ? { storage: input.storage } : {},
|
|
479
|
-
...input.inputMediaType ===
|
|
494
|
+
...input.inputMediaType === void 0 ? {} : { inputMediaType: input.inputMediaType },
|
|
480
495
|
...csv ? { csv } : {},
|
|
481
496
|
size: input.size,
|
|
482
497
|
requestKey: input.requestKey
|
|
@@ -533,7 +548,11 @@ function parseAppRoutineContract(value) {
|
|
|
533
548
|
case "boolean":
|
|
534
549
|
return fields2(value2, ["type"]) ? { type: value2.type } : invalidContract();
|
|
535
550
|
case "file":
|
|
536
|
-
return !fields2(value2, ["type"], ["storage"]) || owns(value2, "storage") && value2.storage !== "local" && value2.storage !== "remote"
|
|
551
|
+
return !fields2(value2, ["type"], ["storage", "path"]) || owns(value2, "storage") && value2.storage !== "local" && value2.storage !== "remote" || owns(value2, "path") && !safeManagedFilePath(value2.path) ? invalidContract() : {
|
|
552
|
+
type: "file",
|
|
553
|
+
...owns(value2, "storage") ? { storage: value2.storage } : {},
|
|
554
|
+
...owns(value2, "path") ? { path: value2.path } : {}
|
|
555
|
+
};
|
|
537
556
|
case "string":
|
|
538
557
|
return !fields2(value2, ["type"], ["maxLength"]) || owns(value2, "maxLength") && (!Number.isSafeInteger(value2.maxLength) || value2.maxLength < 0 || value2.maxLength > 4096) ? invalidContract() : { type: "string", ...owns(value2, "maxLength") ? { maxLength: value2.maxLength } : {} };
|
|
539
558
|
case "integer":
|
|
@@ -580,7 +599,9 @@ function validateRoutineSchemaValue(schema2, value) {
|
|
|
580
599
|
return typeof value != "number" || !(schema2.type === "integer" ? Number.isSafeInteger(value) : Number.isFinite(value)) || schema2.minimum !== void 0 && value < schema2.minimum || schema2.maximum !== void 0 && value > schema2.maximum ? invalidValue() : void 0;
|
|
581
600
|
case "file":
|
|
582
601
|
try {
|
|
583
|
-
jsonData(value)
|
|
602
|
+
jsonData(value);
|
|
603
|
+
let file2 = parseFileDescriptor(value);
|
|
604
|
+
if (schema2.path !== void 0 && file2.path !== schema2.path) return invalidValue();
|
|
584
605
|
} catch {
|
|
585
606
|
return invalidValue();
|
|
586
607
|
}
|
|
@@ -590,14 +611,16 @@ function validateRoutineSchemaValue(schema2, value) {
|
|
|
590
611
|
var ROUTINE_DECLARATION_SCHEMA = "https://schemas.oddington.ai/routine/v1.json", ROUTINE_SOURCE_LIMITS = { files: 2e4, bytes: 256 * 1024 * 1024, declarationBytes: 64 * 1024 };
|
|
591
612
|
var invalid = (field) => {
|
|
592
613
|
throw new Error(`Invalid routine declaration: ${field}`);
|
|
593
|
-
}, object2 = (value, fields5, field) => ((!value || typeof value != "object" || Array.isArray(value) || Object.
|
|
614
|
+
}, 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
615
|
function routineAssetPattern(value) {
|
|
595
616
|
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
617
|
}
|
|
597
618
|
function parseRoutineDeclaration(value) {
|
|
598
619
|
let v = object2(value, ["$schema", "access", "runtime", "retry", "assets"], "fields");
|
|
599
620
|
v.$schema !== ROUTINE_DECLARATION_SCHEMA && invalid("$schema");
|
|
600
|
-
let access = object2(v.access === void 0 ? {} : v.access, ["repositories", "connections", "
|
|
621
|
+
let access = object2(v.access === void 0 ? {} : v.access, ["repositories", "connections", "ai", "files", "publish"], "access");
|
|
622
|
+
Object.hasOwn(access, "ai") && access.ai !== !0 && invalid("access.ai");
|
|
623
|
+
let rawRepositories = access.repositories === void 0 ? {} : access.repositories, repositories = object2(rawRepositories, rawRepositories && typeof rawRepositories == "object" ? Object.keys(rawRepositories) : [], "access.repositories");
|
|
601
624
|
Object.keys(repositories).length > 64 && invalid("access.repositories");
|
|
602
625
|
let grants = {};
|
|
603
626
|
for (let key of Object.keys(repositories).sort())
|
|
@@ -615,7 +638,7 @@ function parseRoutineDeclaration(value) {
|
|
|
615
638
|
access: {
|
|
616
639
|
repositories: grants,
|
|
617
640
|
connections: list(access.connections, "access.connections", void 0, !0),
|
|
618
|
-
|
|
641
|
+
...access.ai === !0 ? { ai: !0 } : {},
|
|
619
642
|
...access.files === void 0 ? {} : { files: access.files },
|
|
620
643
|
publish: list(access.publish, "access.publish", ["asks", "digests"])
|
|
621
644
|
},
|
|
@@ -663,7 +686,7 @@ function assertRoutineFields(value, allowed) {
|
|
|
663
686
|
}
|
|
664
687
|
function parseRoutineAccess(value) {
|
|
665
688
|
try {
|
|
666
|
-
value !== void 0 && assertRoutineJson(value), assertRoutineFields(value ?? {}, ["repositories", "connections", "
|
|
689
|
+
value !== void 0 && assertRoutineJson(value), assertRoutineFields(value ?? {}, ["repositories", "connections", "ai", "files", "publish"]);
|
|
667
690
|
let access = value, names = access?.connections ?? [];
|
|
668
691
|
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
692
|
let parsed = parseRoutineDeclaration({ $schema: ROUTINE_DECLARATION_SCHEMA, access: { ...access, connections: [] } }).access;
|
|
@@ -673,6 +696,52 @@ function parseRoutineAccess(value) {
|
|
|
673
696
|
}
|
|
674
697
|
}
|
|
675
698
|
|
|
699
|
+
var ROUTINE_AI_VALUE_BYTES = 256 * 1024, ROUTINE_AI_EVIDENCE_BYTES = 4 * 1024;
|
|
700
|
+
var RoutineAIError = class extends Error {
|
|
701
|
+
constructor(code) {
|
|
702
|
+
super(code);
|
|
703
|
+
this.code = code;
|
|
704
|
+
this.name = "RoutineAIError";
|
|
705
|
+
}
|
|
706
|
+
code;
|
|
707
|
+
};
|
|
708
|
+
function capture(value, invalid8, oversized) {
|
|
709
|
+
try {
|
|
710
|
+
assertRoutineJson(value);
|
|
711
|
+
let encoded = JSON.stringify(value);
|
|
712
|
+
if (new TextEncoder().encode(encoded).byteLength > ROUTINE_AI_VALUE_BYTES) throw new RoutineAIError(oversized);
|
|
713
|
+
return freezeRoutineMetadata(JSON.parse(encoded));
|
|
714
|
+
} catch (error) {
|
|
715
|
+
throw error instanceof RoutineAIError ? error : new RoutineAIError(invalid8);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
function parseRoutineAIRequest(value) {
|
|
719
|
+
let v = capture(value, "invalid_input", "input_too_large");
|
|
720
|
+
try {
|
|
721
|
+
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();
|
|
722
|
+
let input = typeof v.input == "string" ? v.input : parseFileDescriptor(v.input);
|
|
723
|
+
if (v.kind === "generateText") return freezeRoutineMetadata({ kind: v.kind, instructions: v.instructions, input });
|
|
724
|
+
let schema2 = parseAppRoutineContract({ routine: "ai", input: v.schema, steps: [{ id: "generate" }] }).input;
|
|
725
|
+
return freezeRoutineMetadata({ kind: v.kind, instructions: v.instructions, input, schema: schema2 });
|
|
726
|
+
} catch {
|
|
727
|
+
throw new RoutineAIError("invalid_input");
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
function parseRoutineAIResult(request2, value) {
|
|
731
|
+
let v = capture(value, "invalid_output", "output_too_large");
|
|
732
|
+
try {
|
|
733
|
+
if (request2.kind === "generateText") {
|
|
734
|
+
if (assertRoutineFields(v, ["text"]), typeof v.text != "string" || !v.text.trim()) throw new Error();
|
|
735
|
+
return freezeRoutineMetadata({ text: v.text });
|
|
736
|
+
} else {
|
|
737
|
+
if (assertRoutineFields(v, ["object"]), !Object.hasOwn(v, "object")) throw new Error();
|
|
738
|
+
return validateRoutineSchemaValue(request2.schema, v.object), freezeRoutineMetadata({ object: v.object });
|
|
739
|
+
}
|
|
740
|
+
} catch {
|
|
741
|
+
throw new RoutineAIError("invalid_output");
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
676
745
|
var LIMIT = 256 * 1024, invalid3 = () => {
|
|
677
746
|
throw new AppRoutineContractError("invalid_value");
|
|
678
747
|
}, invalidContract3 = () => {
|
|
@@ -683,7 +752,7 @@ function captureJson(value) {
|
|
|
683
752
|
let encoded = JSON.stringify(value);
|
|
684
753
|
return new TextEncoder().encode(encoded).byteLength > LIMIT ? invalid3() : freezeRoutineMetadata(JSON.parse(encoded));
|
|
685
754
|
}
|
|
686
|
-
function
|
|
755
|
+
function capture2(schema2, value) {
|
|
687
756
|
let captured = captureJson(value);
|
|
688
757
|
return validateRoutineSchemaValue(schema2, captured), captured;
|
|
689
758
|
}
|
|
@@ -709,7 +778,7 @@ function declaration(value, defaults) {
|
|
|
709
778
|
} catch {
|
|
710
779
|
return invalidContract3();
|
|
711
780
|
}
|
|
712
|
-
let parsed = freezeRoutineMetadata(parseAppRoutineContract({ routine: "definition", input: value, steps: [{ id: "inspect" }] }).input), captured = defaults ?
|
|
781
|
+
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
782
|
return schemas.set(builder, { schema: parsed, ...defaults ? { default: captured } : {} }), Object.freeze(builder);
|
|
714
783
|
}
|
|
715
784
|
var schema = {
|
|
@@ -731,7 +800,7 @@ var schema = {
|
|
|
731
800
|
boolean() {
|
|
732
801
|
return declaration({ type: "boolean" });
|
|
733
802
|
}
|
|
734
|
-
}, file = (options = {}) => (assertRoutineFields(options, ["storage"]), declaration({ type: "file", ...options })), definitions = /* @__PURE__ */ new WeakMap(), defineRoutine = (definition) => {
|
|
803
|
+
}, file = (options = {}) => (assertRoutineFields(options, ["storage", "path"]), declaration({ type: "file", ...options })), definitions = /* @__PURE__ */ new WeakMap(), defineRoutine = (definition) => {
|
|
735
804
|
if (assertRoutineFields(definition, ["id", "title", "input", "steps", "result", "access", "responses", "run"]), !isAppRoutineName(definition.id) || definition.title !== void 0 && !routineTitle(definition.title) || typeof definition.run != "function") return invalidContract3();
|
|
736
805
|
let input = definition.input === void 0 ? { schema: empty, default: {} } : schemaMetadata(definition.input), result2 = definition.result === void 0 ? void 0 : schemaMetadata(definition.result).schema, access = parseRoutineAccess(definition.access);
|
|
737
806
|
assertRoutineFields(definition.steps ?? {});
|
|
@@ -794,10 +863,32 @@ function inspectRoutineDefinition(definition) {
|
|
|
794
863
|
}
|
|
795
864
|
function captureRoutineInput(definition, input) {
|
|
796
865
|
let contract = inspectRoutineDefinition(definition);
|
|
797
|
-
return
|
|
866
|
+
return capture2(contract.input, input === void 0 && Object.hasOwn(contract, "inputDefault") ? contract.inputDefault : input);
|
|
798
867
|
}
|
|
799
868
|
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(),
|
|
869
|
+
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) => {
|
|
870
|
+
let operation2 = (async () => {
|
|
871
|
+
if (!active2 || broken || contract.access.ai !== !0) throw new RoutineAIError("access_denied");
|
|
872
|
+
if (!runtime.ai) throw new RoutineAIError("capability_unavailable");
|
|
873
|
+
let request2;
|
|
874
|
+
try {
|
|
875
|
+
assertRoutineFields(value, kind === "generateObject" ? ["instructions", "input", "schema"] : ["instructions", "input"]), request2 = parseRoutineAIRequest({
|
|
876
|
+
kind,
|
|
877
|
+
instructions: value.instructions,
|
|
878
|
+
input: value.input,
|
|
879
|
+
...kind === "generateObject" ? { schema: schemaMetadata(value.schema).schema } : {}
|
|
880
|
+
});
|
|
881
|
+
} catch (error) {
|
|
882
|
+
throw error instanceof RoutineAIError ? error : new RoutineAIError("invalid_input");
|
|
883
|
+
}
|
|
884
|
+
let result3 = await runtime.ai[kind](request2);
|
|
885
|
+
if (!active2 || broken) throw new RoutineAIError("access_denied");
|
|
886
|
+
return parseRoutineAIResult(request2, result3);
|
|
887
|
+
})();
|
|
888
|
+
return aiWork.add(operation2), operation2.then(() => aiWork.delete(operation2), (error) => {
|
|
889
|
+
aiFailure ??= error, broken = !0, aiWork.delete(operation2);
|
|
890
|
+
}), operation2;
|
|
891
|
+
}, called = /* @__PURE__ */ new Set(), reached = /* @__PURE__ */ new Set(), guarded = (value, receiver) => typeof value == "function" ? (...args) => {
|
|
801
892
|
if (!active2 || broken) throw new Error("Run authority ended");
|
|
802
893
|
return guarded(Reflect.apply(value, receiver, args));
|
|
803
894
|
} : value && typeof value == "object" && !(value instanceof Promise) ? new Proxy({ ...value }, {
|
|
@@ -810,6 +901,10 @@ async function executeRoutineDefinition(definition, input, runtime) {
|
|
|
810
901
|
...guarded({
|
|
811
902
|
...runtime,
|
|
812
903
|
run: freezeRoutineMetadata({ ...runtime.run }),
|
|
904
|
+
ai: {
|
|
905
|
+
generateText: (value) => generate("generateText", value),
|
|
906
|
+
generateObject: (value) => generate("generateObject", value)
|
|
907
|
+
},
|
|
813
908
|
connections: { ...runtime.connections, get: async (identity) => {
|
|
814
909
|
if (!active2 || broken || !contract.access.connections?.includes(identity)) throw new Error("Connection unavailable");
|
|
815
910
|
let value = await runtime.connections.get(identity);
|
|
@@ -850,10 +945,11 @@ async function executeRoutineDefinition(definition, input, runtime) {
|
|
|
850
945
|
} catch (error) {
|
|
851
946
|
failure = error, broken = !0;
|
|
852
947
|
}
|
|
853
|
-
accepting = !1, await work
|
|
948
|
+
for (accepting = !1, await work; aiWork.size; ) await Promise.allSettled([...aiWork]);
|
|
949
|
+
active2 = !1;
|
|
854
950
|
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
|
|
951
|
+
if (broken) throw failure ?? aiFailure ?? new AppRoutineContractError("invalid_value");
|
|
952
|
+
if (contract.result !== void 0) return capture2(contract.result, result2);
|
|
857
953
|
if (result2 !== void 0) return invalid3();
|
|
858
954
|
}
|
|
859
955
|
|
|
@@ -1133,19 +1229,19 @@ var invalid6 = () => {
|
|
|
1133
1229
|
}, unavailable = () => {
|
|
1134
1230
|
throw new DataOperationError("unavailable");
|
|
1135
1231
|
}, text2 = (value) => typeof value == "string" && value.length > 0;
|
|
1136
|
-
function
|
|
1232
|
+
function capture3(value, ancestors = /* @__PURE__ */ new Set(), depth = 0) {
|
|
1137
1233
|
if (value === null || typeof value == "string" || typeof value == "boolean" || typeof value == "number" && Number.isFinite(value)) return value;
|
|
1138
1234
|
if (!value || typeof value != "object" || ancestors.has(value) || depth > 32) return invalid6();
|
|
1139
1235
|
ancestors.add(value);
|
|
1140
1236
|
try {
|
|
1141
|
-
return Array.isArray(value) ? historyArray(value).map((item) =>
|
|
1237
|
+
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
1238
|
} finally {
|
|
1143
1239
|
ancestors.delete(value);
|
|
1144
1240
|
}
|
|
1145
1241
|
}
|
|
1146
1242
|
function recordPagination(select) {
|
|
1147
1243
|
function pages(query) {
|
|
1148
|
-
let captured =
|
|
1244
|
+
let captured = capture3(query), names = Object.keys(historyProperties(captured));
|
|
1149
1245
|
for (let name of names) {
|
|
1150
1246
|
let selection2 = historyProperties(captured[name]);
|
|
1151
1247
|
if (Object.hasOwn(selection2, "$")) {
|
|
@@ -1167,7 +1263,7 @@ function recordPagination(select) {
|
|
|
1167
1263
|
return [name, next === void 0 ? selection2 : { ...selection2, $: { ...selection2.$, after: next } }];
|
|
1168
1264
|
})), response2 = await select(request2), page;
|
|
1169
1265
|
try {
|
|
1170
|
-
page =
|
|
1266
|
+
page = capture3(response2);
|
|
1171
1267
|
let keys4 = Object.keys(historyProperties(page));
|
|
1172
1268
|
(keys4.length !== active2.length + 1 || keys4.some((key) => key !== "$meta" && !active2.includes(key))) && unavailable();
|
|
1173
1269
|
let meta = page.$meta;
|
|
@@ -1645,13 +1741,13 @@ var files = {
|
|
|
1645
1741
|
if (intrinsic !== void 0 && input.size !== void 0 && input.size !== intrinsic) throw invalid7();
|
|
1646
1742
|
let name = input.name;
|
|
1647
1743
|
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({
|
|
1744
|
+
let inputMediaType = blob ? declaredOriginalMediaType(input.file.type) : void 0, metadata = parseFileCreateMetadata({
|
|
1649
1745
|
name,
|
|
1650
1746
|
size: intrinsic ?? input.size,
|
|
1651
1747
|
path: input.path,
|
|
1652
1748
|
storage: input.storage,
|
|
1653
1749
|
csv: input.csv,
|
|
1654
|
-
...
|
|
1750
|
+
...inputMediaType === void 0 ? {} : { inputMediaType },
|
|
1655
1751
|
requestKey: options?.requestKey === void 0 ? routineTransport()?.key("files") ?? crypto.randomUUID() : options.requestKey
|
|
1656
1752
|
}, FILE_ORIGINAL_MAX_BYTES);
|
|
1657
1753
|
if (!context2.local && !context2.localFiles && metadata.storage === "local") throw new FileOperationError("unsupported_context", 400);
|
|
@@ -1733,29 +1829,58 @@ var files = {
|
|
|
1733
1829
|
read
|
|
1734
1830
|
};
|
|
1735
1831
|
|
|
1832
|
+
function validDropboxRootPath(value) {
|
|
1833
|
+
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 !== "..");
|
|
1834
|
+
}
|
|
1736
1835
|
function validGoogleScopes(value) {
|
|
1737
1836
|
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
1837
|
}
|
|
1838
|
+
var DROPBOX_FILE_SCOPES = ["account_info.read", "files.metadata.read", "files.content.read", "files.content.write"];
|
|
1839
|
+
function validDropboxFileScopes(value) {
|
|
1840
|
+
return Array.isArray(value) && value.length === DROPBOX_FILE_SCOPES.length && new Set(value).size === value.length && DROPBOX_FILE_SCOPES.every((scope) => value.includes(scope));
|
|
1841
|
+
}
|
|
1739
1842
|
|
|
1740
1843
|
var ROUTINE_CONNECTION_CREDENTIALS_ENV = "OS_ROUTINE_CONNECTION_CREDENTIALS";
|
|
1844
|
+
function parseRoutineConnectionNames(value) {
|
|
1845
|
+
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)
|
|
1846
|
+
throw new Error("Invalid routine connections");
|
|
1847
|
+
return value;
|
|
1848
|
+
}
|
|
1741
1849
|
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
1850
|
function runtimeConnection(lease) {
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1851
|
+
switch (lease.provider) {
|
|
1852
|
+
case "gmail":
|
|
1853
|
+
return {
|
|
1854
|
+
id: `gmail/${lease.name}`,
|
|
1855
|
+
provider: lease.provider,
|
|
1856
|
+
name: lease.name,
|
|
1857
|
+
accountEmail: lease.accountEmail,
|
|
1858
|
+
scopes: [...lease.scopes],
|
|
1859
|
+
accessToken: lease.accessToken,
|
|
1860
|
+
expiresAt: lease.expiresAt
|
|
1861
|
+
};
|
|
1862
|
+
case "airtable":
|
|
1863
|
+
return {
|
|
1864
|
+
id: `airtable/${lease.name}`,
|
|
1865
|
+
provider: lease.provider,
|
|
1866
|
+
name: lease.name,
|
|
1867
|
+
baseId: lease.baseId,
|
|
1868
|
+
accessToken: lease.accessToken,
|
|
1869
|
+
runExpiresAt: lease.runExpiresAt
|
|
1870
|
+
};
|
|
1871
|
+
case "dropbox":
|
|
1872
|
+
return {
|
|
1873
|
+
id: `dropbox/${lease.name}`,
|
|
1874
|
+
provider: lease.provider,
|
|
1875
|
+
name: lease.name,
|
|
1876
|
+
accountId: lease.accountId,
|
|
1877
|
+
access: lease.access,
|
|
1878
|
+
rootPath: lease.rootPath,
|
|
1879
|
+
scopes: [...lease.scopes],
|
|
1880
|
+
accessToken: lease.accessToken,
|
|
1881
|
+
expiresAt: lease.expiresAt
|
|
1882
|
+
};
|
|
1883
|
+
}
|
|
1759
1884
|
}
|
|
1760
1885
|
function parseConnectionLease(raw, now = Date.now()) {
|
|
1761
1886
|
let v = raw;
|
|
@@ -1766,7 +1891,7 @@ function parseConnectionLease(raw, now = Date.now()) {
|
|
|
1766
1891
|
throw new Error("Invalid connection credential");
|
|
1767
1892
|
return v;
|
|
1768
1893
|
}
|
|
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)
|
|
1894
|
+
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
1895
|
return v;
|
|
1771
1896
|
throw new Error("Invalid connection credential");
|
|
1772
1897
|
}
|
|
@@ -2305,9 +2430,9 @@ function parseAnswerableQuestion(value) {
|
|
|
2305
2430
|
let entry = object4(json(value));
|
|
2306
2431
|
keys3(
|
|
2307
2432
|
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));
|
|
2433
|
+
["itemId", "number", "revision", "title", "inboxUrl", "questionId", "routine", "prompt", "response", "answerable", "tags", "recordRefs", "instructions"],
|
|
2434
|
+
["itemId", "number", "revision", "title", "inboxUrl", "questionId", "routine", "prompt", "response", "answerable"]
|
|
2435
|
+
), assert(id2(entry.itemId) && integer(entry.number) && integer(entry.revision) && text4(entry.title) && text4(entry.inboxUrl));
|
|
2311
2436
|
let link = new URL(entry.inboxUrl), slug = link.hostname.slice(0, -13);
|
|
2312
2437
|
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
2438
|
}
|
|
@@ -2841,10 +2966,10 @@ var unavailable3 = () => new InboxOperationError(503, "unavailable"), stale = ()
|
|
|
2841
2966
|
function hostFailure(error, context2) {
|
|
2842
2967
|
return !context2.isCurrent() || context2.expiresAt <= Date.now() || error instanceof InboxContextError && error.status === 401 ? stale() : error instanceof InboxContextError && error.status === 403 ? new InboxOperationError(403, "access_denied") : unavailable3();
|
|
2843
2968
|
}
|
|
2844
|
-
async function send2(operation2, input) {
|
|
2969
|
+
async function send2(operation2, input, admitted) {
|
|
2845
2970
|
let envelope = parseInboxRequest({ operation: operation2, input }), context2;
|
|
2846
2971
|
try {
|
|
2847
|
-
context2 = await resolveAppContext();
|
|
2972
|
+
context2 = admitted ?? await resolveAppContext();
|
|
2848
2973
|
} catch {
|
|
2849
2974
|
throw stale();
|
|
2850
2975
|
}
|
|
@@ -3004,7 +3129,7 @@ var inbox = {
|
|
|
3004
3129
|
let page = await send2("questions.list", {
|
|
3005
3130
|
routines,
|
|
3006
3131
|
...beforeNumber === void 0 ? {} : { beforeNumber }
|
|
3007
|
-
});
|
|
3132
|
+
}, initial);
|
|
3008
3133
|
if (check(), page.questions.some((question2) => question2.inboxUrl !== `https://${initial.workspace}.oddington.ai/inbox/${question2.itemId}` || !routines.includes(question2.routine)) || beforeNumber !== void 0 && page.nextBeforeNumber !== null && page.nextBeforeNumber >= beforeNumber) throw unavailable3();
|
|
3009
3134
|
if (questions.push(...page.questions), page.nextBeforeNumber === null) return questions;
|
|
3010
3135
|
beforeNumber = page.nextBeforeNumber;
|