@oddington/sdk 0.1.35 → 0.1.37
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 +59 -0
- package/dist/browser.js +233 -45
- package/dist/index.js +7 -2
- package/dist/types/packages/sdk/src/public.d.ts +4 -2
- package/dist/types/packages/sdk/src/routine-definition-types.d.ts +1 -0
- package/dist/types/packages/sdk/src/source.d.ts +19 -0
- package/dist/types/shared/app-routine-contract.d.ts +10 -1
- package/dist/types/shared/connection-runtime.d.ts +19 -2
- package/dist/types/shared/repository-targets.d.ts +0 -5
- package/dist/types/shared/routine-ai-contract.d.ts +20 -6
- package/dist/types/shared/routine-ai-output-detail.d.ts +11 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -79,6 +79,27 @@ declared `review` handler crosses the Inbox publication seam as `review`; Inbox
|
|
|
79
79
|
retains the canonical `invoice-import` routine identity separately, without
|
|
80
80
|
deploying or executing that handler.
|
|
81
81
|
|
|
82
|
+
Microsoft Connection configuration has a separate, optional `access` description.
|
|
83
|
+
It preserves supplied strings, including existing `read` and `read-write` labels,
|
|
84
|
+
and does not grant or restrict Microsoft permissions. Management results omit
|
|
85
|
+
this metadata when absent. Setup and safe inspection are documented in the
|
|
86
|
+
[CLI Connections guide](../../apps/cli/README.md#connections).
|
|
87
|
+
|
|
88
|
+
With `access: { ai: true }`, `os.ai.generateText` returns non-empty `{ text }`.
|
|
89
|
+
`os.ai.generateObject` returns `{ object }` validated against the requested
|
|
90
|
+
`schema`, with its inferred type. An invalid answer throws `RoutineAIError`
|
|
91
|
+
with code `invalid_output`, a clear bounded `detail`, and the original final
|
|
92
|
+
answer as optional `text` for routine-owned recovery. The text is bounded at
|
|
93
|
+
256 KiB and excluded from the error message and ordinary JSON serialization.
|
|
94
|
+
The routine can inspect it explicitly, apply its own recovery rules, report
|
|
95
|
+
an item failure and continue; it must not cast a rejected answer to the promised
|
|
96
|
+
type. OS does not coerce, drop fields, repair or retry automatically.
|
|
97
|
+
|
|
98
|
+
An AI call failure can be caught and handled without automatically failing the
|
|
99
|
+
Run. Uncaught routine or step exceptions still fail execution, and outstanding
|
|
100
|
+
AI calls drain before completion. The host retains authority, isolation,
|
|
101
|
+
cancellation and byte limits. Local and remote Runs use the same API.
|
|
102
|
+
|
|
82
103
|
The bounded grammar supports closed objects with required properties, arrays with
|
|
83
104
|
an explicit `maxItems` from 1 to 64, booleans, strings (at most 4096 code points),
|
|
84
105
|
finite numbers and safe integers with optional inclusive bounds, and Files.
|
|
@@ -489,6 +510,44 @@ revision and URL facts. Distinct objects and custom provider values remain.
|
|
|
489
510
|
Helpers need no initialization and perform no I/O, choose no ref, and add no
|
|
490
511
|
changes. File retention and Data validation/publication remain explicit.
|
|
491
512
|
|
|
513
|
+
Microsoft helpers are also exported at the package root, with the corresponding
|
|
514
|
+
`OutlookSourceLocation` and `SharePointSourceLocation` types:
|
|
515
|
+
|
|
516
|
+
```ts
|
|
517
|
+
const messageLocation = source.location.outlook({
|
|
518
|
+
connection: 'outlook/invoices', message: message.id, url: message.webLink,
|
|
519
|
+
// attachment: attachment.id, // Include for attachment bytes; omit for message MIME.
|
|
520
|
+
});
|
|
521
|
+
const itemLocation = source.location.sharepoint({
|
|
522
|
+
connection: 'sharepoint/operations', site: siteId, drive: driveId, item: item.id,
|
|
523
|
+
version: version.id, path: lastKnownPath, url: item.webUrl,
|
|
524
|
+
});
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
Construct these only after successful provider reads establish the coordinates
|
|
528
|
+
for the retained bytes, and save them alongside `source.location.os(file)` using
|
|
529
|
+
ordinary Data publication. Request `Prefer: IdType="ImmutableId"` consistently
|
|
530
|
+
when obtaining Outlook message and attachment IDs. These IDs are case-sensitive
|
|
531
|
+
and survive folder moves within the same mailbox, but not archive-mailbox moves
|
|
532
|
+
or export/reimport. Subjects, filenames and `internetMessageId` are not substitutes.
|
|
533
|
+
Omit `attachment` for an exact message MIME File; supply it for an attachment File.
|
|
534
|
+
|
|
535
|
+
For SharePoint, use the saved Graph site ID, the document library's drive ID and
|
|
536
|
+
the file's driveItem ID. Optional `version` is an actual driveItemVersion ID
|
|
537
|
+
established for those bytes, never an eTag or a guessed version. Omit it when no
|
|
538
|
+
such version is established. Names, paths and URLs do not establish identity.
|
|
539
|
+
Optional `path` is last-known context; `url` is the ordinary HTTPS message web
|
|
540
|
+
link or SharePoint webUrl. Never retain temporary download, upload-session or
|
|
541
|
+
copy-monitor URLs, authorization or credentials in Locations.
|
|
542
|
+
|
|
543
|
+
Outlook merge identity is connection/message/attachment, with whole messages
|
|
544
|
+
distinct from attachments. SharePoint identity is connection/site/drive/item/
|
|
545
|
+
version, with absent versions distinct from present ones. Matching observations
|
|
546
|
+
replace path/link context, including removing omitted context; distinct IDs,
|
|
547
|
+
versions and copies remain separate. Incomplete coordinates gain no inferred
|
|
548
|
+
identity. The retained OS File and hash own exact content; external edits do
|
|
549
|
+
not change an existing Source's bytes. These helpers perform no provider I/O.
|
|
550
|
+
|
|
492
551
|
Read a named repository through the authenticated host context:
|
|
493
552
|
|
|
494
553
|
```ts
|
package/dist/browser.js
CHANGED
|
@@ -221,32 +221,45 @@ function jsonData(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
|
221
221
|
ancestors.delete(value);
|
|
222
222
|
}
|
|
223
223
|
}
|
|
224
|
-
function validateRoutineSchemaValue(schema2, value) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
return
|
|
224
|
+
function validateRoutineSchemaValue(schema2, value, rejected) {
|
|
225
|
+
let type = (value2) => value2 === null ? "null" : Array.isArray(value2) ? "array" : ["object", "boolean", "string", "number"].includes(typeof value2) ? typeof value2 : "other";
|
|
226
|
+
function check(schema3, value2, path) {
|
|
227
|
+
let fail4 = (reason, extra = {}) => (rejected?.({ reason, path: path.length <= 256 ? path : "$", expected: schema3.type, received: type(value2), ...extra }), invalidValue());
|
|
228
|
+
switch (schema3.type) {
|
|
229
|
+
case "object":
|
|
230
|
+
if (!record(value2)) return fail4("wrong_type");
|
|
231
|
+
for (let key of schema3.required ?? []) if (!owns(value2, key)) return fail4("missing_field", {
|
|
232
|
+
path: `${path}.${key}`.length <= 256 ? `${path}.${key}` : "$",
|
|
233
|
+
expected: schema3.properties[key].type,
|
|
234
|
+
received: "missing"
|
|
235
|
+
});
|
|
236
|
+
if (Object.keys(value2).some((key) => !owns(schema3.properties, key))) return fail4("unexpected_field");
|
|
237
|
+
for (let [key, child] of Object.entries(value2)) check(schema3.properties[key], child, `${path}.${key}`);
|
|
238
|
+
return;
|
|
239
|
+
case "array":
|
|
240
|
+
if (!denseArray(value2)) return fail4("wrong_type");
|
|
241
|
+
if (value2.length > schema3.maxItems) return fail4("out_of_bounds", { constraint: "maxItems", limit: schema3.maxItems });
|
|
242
|
+
value2.forEach((child, index) => check(schema3.items, child, `${path}[${index}]`));
|
|
243
|
+
return;
|
|
244
|
+
case "boolean":
|
|
245
|
+
return typeof value2 != "boolean" ? fail4("wrong_type") : void 0;
|
|
246
|
+
case "string":
|
|
247
|
+
return typeof value2 != "string" ? fail4("wrong_type") : Array.from(value2).length > (schema3.maxLength ?? 4096) ? fail4("out_of_bounds", { constraint: "maxLength", limit: schema3.maxLength ?? 4096 }) : void 0;
|
|
248
|
+
case "integer":
|
|
249
|
+
case "number":
|
|
250
|
+
return typeof value2 != "number" || !(schema3.type === "integer" ? Number.isSafeInteger(value2) : Number.isFinite(value2)) ? fail4("wrong_type") : schema3.minimum !== void 0 && value2 < schema3.minimum ? fail4("out_of_bounds", { constraint: "minimum", limit: schema3.minimum }) : schema3.maximum !== void 0 && value2 > schema3.maximum ? fail4("out_of_bounds", { constraint: "maximum", limit: schema3.maximum }) : void 0;
|
|
251
|
+
case "file": {
|
|
252
|
+
let file2;
|
|
253
|
+
try {
|
|
254
|
+
jsonData(value2), file2 = parseFileDescriptor(value2);
|
|
255
|
+
} catch {
|
|
256
|
+
return fail4("invalid_file");
|
|
257
|
+
}
|
|
258
|
+
if (schema3.path !== void 0 && file2.path !== schema3.path) return fail4("invalid_file");
|
|
248
259
|
}
|
|
260
|
+
}
|
|
249
261
|
}
|
|
262
|
+
check(schema2, value, "$");
|
|
250
263
|
}
|
|
251
264
|
|
|
252
265
|
var InboxContractError = class extends Error {
|
|
@@ -695,20 +708,82 @@ function parseRoutineAccess(value) {
|
|
|
695
708
|
}
|
|
696
709
|
}
|
|
697
710
|
|
|
698
|
-
|
|
711
|
+
function parseRoutineAIOutputIssue(value) {
|
|
712
|
+
if (!value || typeof value != "object" || Array.isArray(value)) return;
|
|
713
|
+
let v = value;
|
|
714
|
+
if (["invalid_json", "invalid_response", "empty_text"].includes(String(v.reason)))
|
|
715
|
+
return Object.keys(v).join(",") === "reason" ? { reason: v.reason } : void 0;
|
|
716
|
+
if (!(!["missing_field", "unexpected_field", "wrong_type", "out_of_bounds", "invalid_file"].includes(String(v.reason)) || typeof v.path != "string" || v.path.length > 256 || !/^\$(?:\.[a-z][A-Za-z0-9]*|\[(?:0|[1-9][0-9]*)\])*$/.test(v.path) || !["object", "array", "boolean", "string", "integer", "number", "file"].includes(String(v.expected)) || !["missing", "null", "object", "array", "boolean", "string", "number", "other"].includes(String(v.received)) || Object.keys(v).some((key) => !["reason", "path", "expected", "received", "constraint", "limit"].includes(key)))) {
|
|
717
|
+
if (v.reason === "out_of_bounds") {
|
|
718
|
+
if (!["maxLength", "maxItems", "minimum", "maximum"].includes(String(v.constraint)) || typeof v.limit != "number" || !Number.isFinite(v.limit)) return;
|
|
719
|
+
} else if (Object.hasOwn(v, "constraint") || Object.hasOwn(v, "limit")) return;
|
|
720
|
+
return {
|
|
721
|
+
reason: v.reason,
|
|
722
|
+
path: v.path,
|
|
723
|
+
expected: v.expected,
|
|
724
|
+
received: v.received,
|
|
725
|
+
...v.reason === "out_of_bounds" ? { constraint: v.constraint, limit: v.limit } : {}
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
function routineAIOutputMessage(issue) {
|
|
730
|
+
if (issue.reason === "invalid_json") return "AI replied, but its answer could not be read as JSON.";
|
|
731
|
+
if (issue.reason === "invalid_response") return "AI returned a response OS could not read.";
|
|
732
|
+
if (issue.reason === "empty_text") return "AI returned an empty answer.";
|
|
733
|
+
let field = issue.path === "$" ? "response" : issue.path.slice(issue.path.startsWith("$.") ? 2 : 1), expected = {
|
|
734
|
+
object: "an object",
|
|
735
|
+
array: "a list",
|
|
736
|
+
boolean: "true or false",
|
|
737
|
+
string: "text",
|
|
738
|
+
integer: "a whole number",
|
|
739
|
+
number: "a number",
|
|
740
|
+
file: "a File"
|
|
741
|
+
}[issue.expected], received = {
|
|
742
|
+
missing: "a missing field",
|
|
743
|
+
null: "null",
|
|
744
|
+
object: "an object",
|
|
745
|
+
array: "a list",
|
|
746
|
+
boolean: "true or false",
|
|
747
|
+
string: "text",
|
|
748
|
+
number: "a number",
|
|
749
|
+
other: "an unsupported value"
|
|
750
|
+
}[issue.received];
|
|
751
|
+
if (issue.reason === "missing_field") return `AI response is missing a required field.
|
|
752
|
+
${field}: expected ${expected}; the field was missing.`;
|
|
753
|
+
if (issue.reason === "unexpected_field") return `AI response includes an unexpected field.
|
|
754
|
+
${field}: only the fields declared by the routine are allowed.`;
|
|
755
|
+
if (issue.reason === "invalid_file") return `AI response contains an invalid File.
|
|
756
|
+
${field}: expected a valid File matching the routine's requirements.`;
|
|
757
|
+
if (issue.reason === "out_of_bounds") {
|
|
758
|
+
let constraint = {
|
|
759
|
+
maxLength: `at most ${issue.limit} characters`,
|
|
760
|
+
maxItems: `at most ${issue.limit} items`,
|
|
761
|
+
minimum: `a value of at least ${issue.limit}`,
|
|
762
|
+
maximum: `a value of at most ${issue.limit}`
|
|
763
|
+
}[issue.constraint];
|
|
764
|
+
return `AI response is outside a permitted limit.
|
|
765
|
+
${field}: expected ${constraint}.`;
|
|
766
|
+
}
|
|
767
|
+
return `AI response has the wrong type.
|
|
768
|
+
${field}: expected ${expected}; received ${received}.`;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
var ROUTINE_AI_VALUE_BYTES = 256 * 1024, ROUTINE_AI_RESULT_BYTES = 6 * ROUTINE_AI_VALUE_BYTES + 1024, ROUTINE_AI_EVIDENCE_BYTES = 4 * 1024;
|
|
699
772
|
var RoutineAIError = class extends Error {
|
|
700
|
-
constructor(code) {
|
|
701
|
-
|
|
773
|
+
constructor(code, options = {}) {
|
|
774
|
+
let detail = code === "invalid_output" ? parseRoutineAIOutputIssue(options.detail) : void 0;
|
|
775
|
+
super(detail ? routineAIOutputMessage(detail) : code);
|
|
702
776
|
this.code = code;
|
|
703
|
-
this.name = "RoutineAIError";
|
|
777
|
+
this.name = "RoutineAIError", detail && (this.detail = Object.freeze(detail)), code === "invalid_output" && typeof options.text == "string" && new TextEncoder().encode(options.text).byteLength <= ROUTINE_AI_VALUE_BYTES && Object.defineProperty(this, "text", { value: options.text });
|
|
704
778
|
}
|
|
705
779
|
code;
|
|
780
|
+
detail;
|
|
706
781
|
};
|
|
707
|
-
function capture(value, invalid9, oversized) {
|
|
782
|
+
function capture(value, invalid9, oversized, limit = ROUTINE_AI_VALUE_BYTES) {
|
|
708
783
|
try {
|
|
709
784
|
assertRoutineJson(value);
|
|
710
785
|
let encoded = JSON.stringify(value);
|
|
711
|
-
if (new TextEncoder().encode(encoded).byteLength >
|
|
786
|
+
if (new TextEncoder().encode(encoded).byteLength > limit) throw new RoutineAIError(oversized);
|
|
712
787
|
return freezeRoutineMetadata(JSON.parse(encoded));
|
|
713
788
|
} catch (error) {
|
|
714
789
|
throw error instanceof RoutineAIError ? error : new RoutineAIError(invalid9);
|
|
@@ -727,19 +802,47 @@ function parseRoutineAIRequest(value) {
|
|
|
727
802
|
}
|
|
728
803
|
}
|
|
729
804
|
function parseRoutineAIResult(request2, value) {
|
|
730
|
-
let v
|
|
805
|
+
let v;
|
|
731
806
|
try {
|
|
732
|
-
if (
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
807
|
+
if (v = capture(
|
|
808
|
+
value,
|
|
809
|
+
"invalid_output",
|
|
810
|
+
"output_too_large",
|
|
811
|
+
request2.kind === "generateText" ? ROUTINE_AI_RESULT_BYTES : ROUTINE_AI_VALUE_BYTES
|
|
812
|
+
), assertRoutineFields(v, request2.kind === "generateText" ? ["text"] : ["object"]), request2.kind === "generateText") {
|
|
813
|
+
if (typeof v.text != "string") throw new Error();
|
|
814
|
+
return acceptText(request2, v.text);
|
|
738
815
|
}
|
|
816
|
+
if (!Object.hasOwn(v, "object")) throw new Error();
|
|
817
|
+
} catch (error) {
|
|
818
|
+
throw error instanceof RoutineAIError ? error : new RoutineAIError("invalid_output", { detail: { reason: "invalid_response" } });
|
|
819
|
+
}
|
|
820
|
+
return validateObject(request2.schema, v.object), freezeRoutineMetadata({ object: v.object });
|
|
821
|
+
}
|
|
822
|
+
function validateObject(schema2, object5, text5) {
|
|
823
|
+
let detail = { reason: "invalid_response" };
|
|
824
|
+
try {
|
|
825
|
+
validateRoutineSchemaValue(schema2, object5, (issue) => {
|
|
826
|
+
detail = issue;
|
|
827
|
+
});
|
|
739
828
|
} catch {
|
|
740
|
-
throw new RoutineAIError("invalid_output");
|
|
829
|
+
throw new RoutineAIError("invalid_output", { ...text5 === void 0 ? {} : { text: text5 }, detail });
|
|
741
830
|
}
|
|
742
831
|
}
|
|
832
|
+
function acceptText(request2, text5) {
|
|
833
|
+
if (new TextEncoder().encode(text5).byteLength > ROUTINE_AI_VALUE_BYTES) throw new RoutineAIError("output_too_large");
|
|
834
|
+
if (request2.kind === "generateText") {
|
|
835
|
+
if (!text5.trim()) throw new RoutineAIError("invalid_output", { text: text5, detail: { reason: "empty_text" } });
|
|
836
|
+
return freezeRoutineMetadata({ text: text5 });
|
|
837
|
+
}
|
|
838
|
+
let object5;
|
|
839
|
+
try {
|
|
840
|
+
object5 = JSON.parse(text5);
|
|
841
|
+
} catch {
|
|
842
|
+
throw new RoutineAIError("invalid_output", { text: text5, detail: { reason: "invalid_json" } });
|
|
843
|
+
}
|
|
844
|
+
return validateObject(request2.schema, object5, text5), capture({ object: object5 }, "invalid_output", "output_too_large");
|
|
845
|
+
}
|
|
743
846
|
|
|
744
847
|
var LIMIT = 256 * 1024, invalid3 = () => {
|
|
745
848
|
throw new AppRoutineContractError("invalid_value");
|
|
@@ -865,7 +968,7 @@ function captureRoutineInput(definition, input) {
|
|
|
865
968
|
return capture2(contract.input, input === void 0 && Object.hasOwn(contract, "inputDefault") ? contract.inputDefault : input);
|
|
866
969
|
}
|
|
867
970
|
async function executeRoutineDefinition(definition, input, runtime) {
|
|
868
|
-
let captured = captureRoutineInput(definition, input), { contract, steps, run } = definitions.get(definition), active2 = !0, accepting = !0, broken = !1, work = Promise.resolve(),
|
|
971
|
+
let captured = captureRoutineInput(definition, input), { contract, steps, run } = definitions.get(definition), active2 = !0, accepting = !0, broken = !1, work = Promise.resolve(), aiWork = /* @__PURE__ */ new Set(), generate = (kind, value) => {
|
|
869
972
|
let operation2 = (async () => {
|
|
870
973
|
if (!active2 || broken || contract.access.ai !== !0) throw new RoutineAIError("access_denied");
|
|
871
974
|
if (!runtime.ai) throw new RoutineAIError("capability_unavailable");
|
|
@@ -884,9 +987,7 @@ async function executeRoutineDefinition(definition, input, runtime) {
|
|
|
884
987
|
if (!active2 || broken) throw new RoutineAIError("access_denied");
|
|
885
988
|
return parseRoutineAIResult(request2, result3);
|
|
886
989
|
})();
|
|
887
|
-
return aiWork.add(operation2), operation2.then(() => aiWork.delete(operation2), (
|
|
888
|
-
aiFailure ??= error, broken = !0, aiWork.delete(operation2);
|
|
889
|
-
}), operation2;
|
|
990
|
+
return aiWork.add(operation2), operation2.then(() => aiWork.delete(operation2), () => aiWork.delete(operation2)), operation2;
|
|
890
991
|
}, called = /* @__PURE__ */ new Set(), reached = /* @__PURE__ */ new Set(), guarded = (value, receiver) => typeof value == "function" ? (...args) => {
|
|
891
992
|
if (!active2 || broken) throw new Error("Run authority ended");
|
|
892
993
|
return guarded(Reflect.apply(value, receiver, args));
|
|
@@ -950,7 +1051,7 @@ async function executeRoutineDefinition(definition, input, runtime) {
|
|
|
950
1051
|
for (accepting = !1, await work; aiWork.size; ) await Promise.allSettled([...aiWork]);
|
|
951
1052
|
active2 = !1;
|
|
952
1053
|
for (let step of contract.steps) reached.has(step.id) || await runtime.step(step.id, { status: "skipped" });
|
|
953
|
-
if (broken) throw failure ??
|
|
1054
|
+
if (broken) throw failure ?? new AppRoutineContractError("invalid_value");
|
|
954
1055
|
if (contract.result !== void 0) return capture2(contract.result, result2);
|
|
955
1056
|
if (result2 !== void 0) return invalid3();
|
|
956
1057
|
}
|
|
@@ -1859,6 +1960,37 @@ var files = {
|
|
|
1859
1960
|
function validDropboxRootPath(value) {
|
|
1860
1961
|
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 !== "..");
|
|
1861
1962
|
}
|
|
1963
|
+
function validMicrosoftId(value) {
|
|
1964
|
+
return typeof value == "string" && /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i.test(value);
|
|
1965
|
+
}
|
|
1966
|
+
function validMicrosoftMailbox(value) {
|
|
1967
|
+
return typeof value == "string" && value.length <= 254 && /^[^\s@/?#\\\x00-\x1f\x7f]+@[^\s@/?#\\\x00-\x1f\x7f]+\.[^\s@/?#\\\x00-\x1f\x7f]+$/.test(value);
|
|
1968
|
+
}
|
|
1969
|
+
function sharePointHost(value) {
|
|
1970
|
+
return /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.sharepoint\.com$/.test(value);
|
|
1971
|
+
}
|
|
1972
|
+
function canonicalSharePointSiteUrl(value) {
|
|
1973
|
+
if (!(typeof value != "string" || value.length > 2048 || /[\s\\?#\x00-\x1f\x7f]/.test(value) || !/^https:\/\/[^/:@]+(?:\/|$)/i.test(value)))
|
|
1974
|
+
try {
|
|
1975
|
+
let url = new URL(value);
|
|
1976
|
+
if (url.protocol !== "https:" || !sharePointHost(url.hostname) || url.username || url.password || url.port) return;
|
|
1977
|
+
let rawPath = value.replace(/^https:\/\/[^/]+/i, "") || "/", path = rawPath.endsWith("/") ? rawPath.slice(0, -1) : rawPath, segments = path.slice(1).split("/");
|
|
1978
|
+
if (path && segments.some((segment) => !segment)) return;
|
|
1979
|
+
let encoded = path ? segments.map((segment) => {
|
|
1980
|
+
let decoded = decodeURIComponent(segment);
|
|
1981
|
+
if (decoded === "." || decoded === ".." || /[/%\\?#\x00-\x1f\x7f]/.test(decoded)) throw new Error();
|
|
1982
|
+
return encodeURIComponent(decoded);
|
|
1983
|
+
}).join("/") : "", canonical = `https://${url.hostname}/${encoded}`;
|
|
1984
|
+
return canonical.length <= 2048 ? canonical : void 0;
|
|
1985
|
+
} catch {
|
|
1986
|
+
return;
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
function validSharePointSiteId(value, siteUrl) {
|
|
1990
|
+
if (typeof value != "string" || value.length > 512) return !1;
|
|
1991
|
+
let [host, collection, site, extra] = value.split(",");
|
|
1992
|
+
return extra === void 0 && host === new URL(siteUrl).hostname && validMicrosoftId(collection) && validMicrosoftId(site);
|
|
1993
|
+
}
|
|
1862
1994
|
function validGoogleScopes(value) {
|
|
1863
1995
|
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;
|
|
1864
1996
|
}
|
|
@@ -1869,13 +2001,32 @@ function validDropboxFileScopes(value) {
|
|
|
1869
2001
|
|
|
1870
2002
|
var ROUTINE_CONNECTION_CREDENTIALS_ENV = "OS_ROUTINE_CONNECTION_CREDENTIALS";
|
|
1871
2003
|
function parseRoutineConnectionNames(value) {
|
|
1872
|
-
if (!Array.isArray(value) || value.some((name2) => typeof name2 != "string" || !/^(gmail|airtable|dropbox)\/[a-z0-9][a-z0-9-]{0,62}$/.test(name2)) || new Set(value).size !== value.length)
|
|
2004
|
+
if (!Array.isArray(value) || value.some((name2) => typeof name2 != "string" || !/^(gmail|airtable|dropbox|outlook|sharepoint)\/[a-z0-9][a-z0-9-]{0,62}$/.test(name2)) || new Set(value).size !== value.length)
|
|
1873
2005
|
throw new Error("Invalid routine connections");
|
|
1874
2006
|
return value;
|
|
1875
2007
|
}
|
|
1876
2008
|
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);
|
|
1877
2009
|
function runtimeConnection(lease) {
|
|
1878
2010
|
switch (lease.provider) {
|
|
2011
|
+
case "outlook":
|
|
2012
|
+
return {
|
|
2013
|
+
id: `outlook/${lease.name}`,
|
|
2014
|
+
provider: lease.provider,
|
|
2015
|
+
name: lease.name,
|
|
2016
|
+
mailboxEmail: lease.mailboxEmail,
|
|
2017
|
+
accessToken: lease.accessToken,
|
|
2018
|
+
expiresAt: lease.expiresAt
|
|
2019
|
+
};
|
|
2020
|
+
case "sharepoint":
|
|
2021
|
+
return {
|
|
2022
|
+
id: `sharepoint/${lease.name}`,
|
|
2023
|
+
provider: lease.provider,
|
|
2024
|
+
name: lease.name,
|
|
2025
|
+
siteId: lease.siteId,
|
|
2026
|
+
siteUrl: lease.siteUrl,
|
|
2027
|
+
accessToken: lease.accessToken,
|
|
2028
|
+
expiresAt: lease.expiresAt
|
|
2029
|
+
};
|
|
1879
2030
|
case "gmail":
|
|
1880
2031
|
return {
|
|
1881
2032
|
id: `gmail/${lease.name}`,
|
|
@@ -1913,6 +2064,11 @@ function parseConnectionLease(raw, now = Date.now()) {
|
|
|
1913
2064
|
let v = raw;
|
|
1914
2065
|
if (!v || typeof v != "object" || Array.isArray(v) || !connectionUuid(v.connectionId) || !connectionUuid(v.runId) || typeof v.name != "string" || !/^[a-z0-9][a-z0-9-]{0,62}$/.test(v.name) || typeof v.accessToken != "string" || !/^[A-Za-z0-9._~+/-]{1,8192}={0,2}$/.test(v.accessToken))
|
|
1915
2066
|
throw new Error("Invalid connection credential");
|
|
2067
|
+
if (v.provider === "outlook" || v.provider === "sharepoint") {
|
|
2068
|
+
if (!(v.provider === "outlook" ? Object.keys(v).sort().join(",") === "accessToken,connectionId,expiresAt,mailboxEmail,name,provider,runId" && validMicrosoftMailbox(v.mailboxEmail) : Object.keys(v).sort().join(",") === "accessToken,connectionId,expiresAt,name,provider,runId,siteId,siteUrl" && typeof v.siteUrl == "string" && canonicalSharePointSiteUrl(v.siteUrl) === v.siteUrl && validSharePointSiteId(v.siteId, v.siteUrl)) || typeof v.expiresAt != "string" || !Number.isFinite(Date.parse(v.expiresAt)) || Date.parse(v.expiresAt) <= now + 6e4 || Date.parse(v.expiresAt) > now + 36e5)
|
|
2069
|
+
throw new Error("Invalid connection credential");
|
|
2070
|
+
return v;
|
|
2071
|
+
}
|
|
1916
2072
|
if (v.provider === "gmail") {
|
|
1917
2073
|
if (Object.keys(v).sort().join(",") !== "accessToken,accountEmail,connectionId,expiresAt,name,provider,runId,scopes" || typeof v.accountEmail != "string" || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v.accountEmail) || !validGoogleScopes(v.scopes) || typeof v.expiresAt != "string" || !Number.isFinite(Date.parse(v.expiresAt)) || Date.parse(v.expiresAt) <= now + 6e4 || Date.parse(v.expiresAt) > now + 36e5)
|
|
1918
2074
|
throw new Error("Invalid connection credential");
|
|
@@ -2180,7 +2336,17 @@ var connections = /* @__PURE__ */ runtimeConnections();
|
|
|
2180
2336
|
var LOCAL_ROUTINE_VALUE_BYTES = 256 * 1024;
|
|
2181
2337
|
|
|
2182
2338
|
function locationIdentity(location) {
|
|
2183
|
-
let coordinates = location.provider === "os" ? [location.file] : location.provider === "gmail" ? [location.connection, location.message, location.attachment] : location.provider === "dropbox" ? [location.connection, location.file] :
|
|
2339
|
+
let coordinates = location.provider === "os" ? [location.file] : location.provider === "gmail" ? [location.connection, location.message, location.attachment] : location.provider === "dropbox" ? [location.connection, location.file] : location.provider === "outlook" ? [
|
|
2340
|
+
location.connection,
|
|
2341
|
+
location.message,
|
|
2342
|
+
...location.attachment === void 0 ? [] : [location.attachment]
|
|
2343
|
+
] : location.provider === "sharepoint" ? [
|
|
2344
|
+
location.connection,
|
|
2345
|
+
location.site,
|
|
2346
|
+
location.drive,
|
|
2347
|
+
location.item,
|
|
2348
|
+
...location.version === void 0 ? [] : [location.version]
|
|
2349
|
+
] : void 0;
|
|
2184
2350
|
if (coordinates?.every((value) => typeof value == "string" && value.length > 0))
|
|
2185
2351
|
return JSON.stringify([location.provider, ...coordinates]);
|
|
2186
2352
|
}
|
|
@@ -2219,6 +2385,27 @@ var source = {
|
|
|
2219
2385
|
...input.path === void 0 ? {} : { path: input.path },
|
|
2220
2386
|
...input.url === void 0 ? {} : { url: input.url }
|
|
2221
2387
|
};
|
|
2388
|
+
},
|
|
2389
|
+
outlook(input) {
|
|
2390
|
+
return {
|
|
2391
|
+
provider: "outlook",
|
|
2392
|
+
connection: input.connection,
|
|
2393
|
+
message: input.message,
|
|
2394
|
+
...input.attachment === void 0 ? {} : { attachment: input.attachment },
|
|
2395
|
+
...input.url === void 0 ? {} : { url: input.url }
|
|
2396
|
+
};
|
|
2397
|
+
},
|
|
2398
|
+
sharepoint(input) {
|
|
2399
|
+
return {
|
|
2400
|
+
provider: "sharepoint",
|
|
2401
|
+
connection: input.connection,
|
|
2402
|
+
site: input.site,
|
|
2403
|
+
drive: input.drive,
|
|
2404
|
+
item: input.item,
|
|
2405
|
+
...input.version === void 0 ? {} : { version: input.version },
|
|
2406
|
+
...input.path === void 0 ? {} : { path: input.path },
|
|
2407
|
+
...input.url === void 0 ? {} : { url: input.url }
|
|
2408
|
+
};
|
|
2222
2409
|
}
|
|
2223
2410
|
},
|
|
2224
2411
|
locations: {
|
|
@@ -4016,6 +4203,7 @@ export {
|
|
|
4016
4203
|
FileOperationError,
|
|
4017
4204
|
InboxContractError,
|
|
4018
4205
|
InboxOperationError,
|
|
4206
|
+
RoutineAIError,
|
|
4019
4207
|
app,
|
|
4020
4208
|
asksBlock,
|
|
4021
4209
|
auth,
|