@oddington/sdk 0.1.34 → 0.1.36
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 +26 -7
- package/dist/browser.js +410 -303
- package/dist/index.js +8 -3
- package/dist/types/packages/sdk/src/public.d.ts +4 -26
- package/dist/types/packages/sdk/src/routine-definition-types.d.ts +1 -0
- package/dist/types/shared/app-routine-contract.d.ts +10 -1
- package/dist/types/shared/files-contract.d.ts +30 -1
- package/dist/types/shared/routine-ai-contract.d.ts +20 -6
- package/dist/types/shared/routine-ai-output-detail.d.ts +11 -0
- package/dist/types/shared/routine-deployment-contract.d.ts +4 -4
- package/package.json +1 -1
- package/dist/types/shared/record-write.d.ts +0 -33
package/dist/browser.js
CHANGED
|
@@ -1,14 +1,49 @@
|
|
|
1
|
+
var fileValidationReasons = {
|
|
2
|
+
invalid_metadata: "File metadata must contain only the supported fields.",
|
|
3
|
+
invalid_name: "File name must be 1\u2013255 characters, with no reserved names, path separators, control characters or trailing spaces/dots.",
|
|
4
|
+
invalid_path: "File path must contain safe relative folder names and cannot start with .os.",
|
|
5
|
+
invalid_storage: "File storage must be local or remote.",
|
|
6
|
+
invalid_media_type: "File media type must name one valid type/subtype.",
|
|
7
|
+
invalid_size: "File size must be a non-negative safe integer byte count.",
|
|
8
|
+
invalid_request_key: "File request key must contain 1\u2013256 characters, with no surrounding whitespace or control characters.",
|
|
9
|
+
invalid_enrichment_name: "Enrichment name must be a safe filename and cannot be original or original.*.",
|
|
10
|
+
invalid_replacement: "Enrichment replacement must be a SHA-256 hash of 64 lowercase hexadecimal characters.",
|
|
11
|
+
invalid_bytes: "File content must contain byte chunks."
|
|
12
|
+
};
|
|
13
|
+
function parseFileErrorDetail(value) {
|
|
14
|
+
if (!value || typeof value != "object" || Array.isArray(value)) return;
|
|
15
|
+
let v = value;
|
|
16
|
+
if (typeof v.reason != "string") return;
|
|
17
|
+
if (Object.hasOwn(fileValidationReasons, v.reason))
|
|
18
|
+
return Object.keys(v).length === 1 ? { reason: v.reason } : void 0;
|
|
19
|
+
if (v.reason !== "size_mismatch" && v.reason !== "size_limit") return;
|
|
20
|
+
let count = v.reason === "size_mismatch" ? "expectedBytes" : "limitBytes";
|
|
21
|
+
if (!(Object.keys(v).some((key) => !["reason", count, "receivedBytes", "receivedAtLeast"].includes(key)) || !Number.isSafeInteger(v[count]) || v[count] < 0 || !Number.isSafeInteger(v.receivedBytes) || v.receivedBytes < 0 || v.receivedAtLeast !== void 0 && v.receivedAtLeast !== !0))
|
|
22
|
+
return {
|
|
23
|
+
reason: v.reason,
|
|
24
|
+
[count]: v[count],
|
|
25
|
+
receivedBytes: v.receivedBytes,
|
|
26
|
+
...v.receivedAtLeast ? { receivedAtLeast: !0 } : {}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function fileErrorExplanation(detail) {
|
|
30
|
+
return detail.reason === "size_mismatch" ? `File byte count mismatch: expected ${detail.expectedBytes} bytes; received ${detail.receivedAtLeast ? "at least " : ""}${detail.receivedBytes} bytes.` : detail.reason === "size_limit" ? `File size exceeds the ${detail.limitBytes}-byte limit; received ${detail.receivedAtLeast ? "at least " : ""}${detail.receivedBytes} bytes.` : fileValidationReasons[detail.reason];
|
|
31
|
+
}
|
|
1
32
|
var FileOperationError = class extends Error {
|
|
2
|
-
constructor(code, status, retryable = !1) {
|
|
3
|
-
|
|
33
|
+
constructor(code, status, retryable = !1, detail) {
|
|
34
|
+
let safe = ["invalid_input", "request_too_large"].includes(code) ? parseFileErrorDetail(detail) : void 0;
|
|
35
|
+
super(safe ? `${code}: ${fileErrorExplanation(safe)}` : code);
|
|
4
36
|
this.code = code;
|
|
5
37
|
this.status = status;
|
|
6
38
|
this.retryable = retryable;
|
|
39
|
+
safe && (this.detail = Object.freeze(safe));
|
|
7
40
|
}
|
|
8
41
|
code;
|
|
9
42
|
status;
|
|
10
43
|
retryable;
|
|
11
|
-
|
|
44
|
+
detail;
|
|
45
|
+
};
|
|
46
|
+
var FILE_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/, FILE_ORIGINAL_MAX_BYTES = 5 * 1024 ** 3;
|
|
12
47
|
function parseFileDescriptor(value) {
|
|
13
48
|
let invalid9 = () => {
|
|
14
49
|
throw new FileOperationError("unavailable", 503, !0);
|
|
@@ -34,13 +69,22 @@ function safeEnrichmentName(value) {
|
|
|
34
69
|
return safeFileName(value) && !/^original(?:\.|$)/i.test(value);
|
|
35
70
|
}
|
|
36
71
|
function parseFileEnrichMetadata(value, maxBytes) {
|
|
37
|
-
let invalid9 = () => {
|
|
38
|
-
throw new FileOperationError("invalid_input", 400);
|
|
72
|
+
let invalid9 = (reason = "invalid_metadata") => {
|
|
73
|
+
throw new FileOperationError("invalid_input", 400, !1, { reason });
|
|
39
74
|
};
|
|
40
75
|
if (!value || typeof value != "object" || Array.isArray(value)) return invalid9();
|
|
41
76
|
let d = value;
|
|
42
|
-
if (Object.keys(d).some((key) => !["name", "size", "mediaType", "replace"].includes(key))
|
|
43
|
-
if (d.
|
|
77
|
+
if (Object.keys(d).some((key) => !["name", "size", "mediaType", "replace"].includes(key))) return invalid9();
|
|
78
|
+
if (!safeEnrichmentName(d.name)) return invalid9("invalid_enrichment_name");
|
|
79
|
+
if (!Number.isSafeInteger(d.size) || d.size < 0) return invalid9("invalid_size");
|
|
80
|
+
if (d.mediaType !== void 0 && (typeof d.mediaType != "string" || d.mediaType.length > 255 || /[\u0000-\u001f\u007f]/.test(d.mediaType))) return invalid9("invalid_media_type");
|
|
81
|
+
if (d.replace !== void 0 && (typeof d.replace != "string" || !/^[0-9a-f]{64}$/.test(d.replace))) return invalid9("invalid_replacement");
|
|
82
|
+
if (d.size > maxBytes) throw new FileOperationError(
|
|
83
|
+
"request_too_large",
|
|
84
|
+
413,
|
|
85
|
+
!1,
|
|
86
|
+
{ reason: "size_limit", limitBytes: maxBytes, receivedBytes: d.size }
|
|
87
|
+
);
|
|
44
88
|
return d;
|
|
45
89
|
}
|
|
46
90
|
function safeFileName(value) {
|
|
@@ -54,9 +98,9 @@ function safeManagedFilePath(value) {
|
|
|
54
98
|
}
|
|
55
99
|
function declaredOriginalMediaType(value) {
|
|
56
100
|
if (typeof value != "string" || value.length > 255 || /[\u0000-\u001f\u007f-\u009f]/.test(value))
|
|
57
|
-
throw new FileOperationError("invalid_input", 400);
|
|
101
|
+
throw new FileOperationError("invalid_input", 400, !1, { reason: "invalid_media_type" });
|
|
58
102
|
let normalized = value.split(";", 1)[0].trim().toLowerCase(), token = "[a-z0-9!#$%&'+.^_`|~-]+";
|
|
59
|
-
if (!new RegExp(`^${token}/${token}$`).test(normalized)) throw new FileOperationError("invalid_input", 400);
|
|
103
|
+
if (!new RegExp(`^${token}/${token}$`).test(normalized)) throw new FileOperationError("invalid_input", 400, !1, { reason: "invalid_media_type" });
|
|
60
104
|
return normalized;
|
|
61
105
|
}
|
|
62
106
|
function isCanonicalOriginalMediaType(value) {
|
|
@@ -67,18 +111,25 @@ function isCanonicalOriginalMediaType(value) {
|
|
|
67
111
|
}
|
|
68
112
|
}
|
|
69
113
|
function parseFileCreateMetadata(value, maxBytes) {
|
|
70
|
-
let invalid9 = () => {
|
|
71
|
-
throw new FileOperationError("invalid_input", 400);
|
|
114
|
+
let invalid9 = (reason = "invalid_metadata") => {
|
|
115
|
+
throw new FileOperationError("invalid_input", 400, !1, { reason });
|
|
72
116
|
};
|
|
73
117
|
if (!value || typeof value != "object" || Array.isArray(value)) return invalid9();
|
|
74
118
|
let input = value;
|
|
75
119
|
if (Object.keys(input).some((key) => !["name", "path", "storage", "mediaType", "size", "requestKey"].includes(key))) return invalid9();
|
|
76
120
|
let path = input.path === void 0 ? "Files" : input.path;
|
|
77
|
-
if (!safeFileName(input.name)
|
|
121
|
+
if (!safeFileName(input.name)) return invalid9("invalid_name");
|
|
122
|
+
if (!safeManagedFilePath(path)) return invalid9("invalid_path");
|
|
123
|
+
if (input.storage !== void 0 && input.storage !== "remote" && input.storage !== "local") return invalid9("invalid_storage");
|
|
78
124
|
let mediaType = input.mediaType === void 0 ? "application/octet-stream" : declaredOriginalMediaType(input.mediaType);
|
|
79
|
-
if (!Number.isSafeInteger(input.size) || input.size < 0) return invalid9();
|
|
80
|
-
if (input.size > maxBytes) throw new FileOperationError(
|
|
81
|
-
|
|
125
|
+
if (!Number.isSafeInteger(input.size) || input.size < 0) return invalid9("invalid_size");
|
|
126
|
+
if (input.size > maxBytes) throw new FileOperationError(
|
|
127
|
+
"request_too_large",
|
|
128
|
+
413,
|
|
129
|
+
!1,
|
|
130
|
+
{ reason: "size_limit", limitBytes: maxBytes, receivedBytes: input.size }
|
|
131
|
+
);
|
|
132
|
+
return typeof input.requestKey != "string" || input.requestKey.length < 1 || input.requestKey.length > 256 || input.requestKey !== input.requestKey.trim() || /[\u0000-\u001f\u007f]/.test(input.requestKey) ? invalid9("invalid_request_key") : {
|
|
82
133
|
name: input.name,
|
|
83
134
|
path,
|
|
84
135
|
...input.storage ? { storage: input.storage } : {},
|
|
@@ -88,6 +139,129 @@ function parseFileCreateMetadata(value, maxBytes) {
|
|
|
88
139
|
};
|
|
89
140
|
}
|
|
90
141
|
|
|
142
|
+
var AppRoutineContractError = class extends Error {
|
|
143
|
+
constructor(code) {
|
|
144
|
+
super(code);
|
|
145
|
+
this.code = code;
|
|
146
|
+
}
|
|
147
|
+
code;
|
|
148
|
+
}, propertyName = /^[a-z][A-Za-z0-9]*(?![\s\S])/, stepId = /^[a-z0-9]+(?:-[a-z0-9]+)*(?![\s\S])/, routineName = /^[a-z0-9][a-z0-9-]{0,62}(?![\s\S])/;
|
|
149
|
+
function isAppRoutineName(value) {
|
|
150
|
+
return typeof value == "string" && routineName.test(value);
|
|
151
|
+
}
|
|
152
|
+
var owns = (value, key) => Object.hasOwn(value, key), invalidContract = () => {
|
|
153
|
+
throw new AppRoutineContractError("invalid_contract");
|
|
154
|
+
}, invalidValue = () => {
|
|
155
|
+
throw new AppRoutineContractError("invalid_value");
|
|
156
|
+
};
|
|
157
|
+
function record(value) {
|
|
158
|
+
return value !== null && typeof value == "object" && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
|
|
159
|
+
}
|
|
160
|
+
function fields(value, required, optional = []) {
|
|
161
|
+
return required.every((key) => owns(value, key)) && Object.keys(value).every((key) => required.includes(key) || optional.includes(key));
|
|
162
|
+
}
|
|
163
|
+
function denseArray(value) {
|
|
164
|
+
return Array.isArray(value) && Object.keys(value).length === value.length && Array.from({ length: value.length }, (_, index) => owns(value, String(index))).every(Boolean);
|
|
165
|
+
}
|
|
166
|
+
function parseAppRoutineContract(value) {
|
|
167
|
+
let properties = 0;
|
|
168
|
+
function schema2(value2, depth = 1) {
|
|
169
|
+
if (depth > 8 || !record(value2)) return invalidContract();
|
|
170
|
+
switch (value2.type) {
|
|
171
|
+
case "object": {
|
|
172
|
+
if (!fields(value2, ["type", "properties", "additionalProperties"], ["required"]) || value2.additionalProperties !== !1 || !record(value2.properties)) return invalidContract();
|
|
173
|
+
let entries = Object.entries(value2.properties);
|
|
174
|
+
if (properties += entries.length, properties > 64 || entries.some(([key]) => !propertyName.test(key))) return invalidContract();
|
|
175
|
+
let required;
|
|
176
|
+
if (owns(value2, "required")) {
|
|
177
|
+
if (!denseArray(value2.required) || value2.required.some((key) => typeof key != "string" || !owns(value2.properties, key)) || new Set(value2.required).size !== value2.required.length) return invalidContract();
|
|
178
|
+
required = [...value2.required];
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
type: "object",
|
|
182
|
+
properties: Object.fromEntries(entries.map(([key, child]) => [key, schema2(child, depth + 1)])),
|
|
183
|
+
...required === void 0 ? {} : { required },
|
|
184
|
+
additionalProperties: !1
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
case "array":
|
|
188
|
+
return !fields(value2, ["type", "items", "maxItems"]) || !Number.isSafeInteger(value2.maxItems) || value2.maxItems < 1 || value2.maxItems > 64 ? invalidContract() : { type: "array", items: schema2(value2.items, depth + 1), maxItems: value2.maxItems };
|
|
189
|
+
case "boolean":
|
|
190
|
+
return fields(value2, ["type"]) ? { type: value2.type } : invalidContract();
|
|
191
|
+
case "file":
|
|
192
|
+
return !fields(value2, ["type"], ["storage", "path"]) || owns(value2, "storage") && value2.storage !== "local" && value2.storage !== "remote" || owns(value2, "path") && !safeManagedFilePath(value2.path) ? invalidContract() : {
|
|
193
|
+
type: "file",
|
|
194
|
+
...owns(value2, "storage") ? { storage: value2.storage } : {},
|
|
195
|
+
...owns(value2, "path") ? { path: value2.path } : {}
|
|
196
|
+
};
|
|
197
|
+
case "string":
|
|
198
|
+
return !fields(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 } : {} };
|
|
199
|
+
case "integer":
|
|
200
|
+
case "number": {
|
|
201
|
+
let validNumber = value2.type === "integer" ? Number.isSafeInteger : Number.isFinite;
|
|
202
|
+
return !fields(value2, ["type"], ["minimum", "maximum"]) || owns(value2, "minimum") && !validNumber(value2.minimum) || owns(value2, "maximum") && !validNumber(value2.maximum) || owns(value2, "minimum") && owns(value2, "maximum") && value2.minimum > value2.maximum ? invalidContract() : {
|
|
203
|
+
type: value2.type,
|
|
204
|
+
...owns(value2, "minimum") ? { minimum: value2.minimum } : {},
|
|
205
|
+
...owns(value2, "maximum") ? { maximum: value2.maximum } : {}
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
default:
|
|
209
|
+
return invalidContract();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (!record(value) || !fields(value, ["routine", "input", "steps"], ["result"]) || !isAppRoutineName(value.routine) || !denseArray(value.steps) || value.steps.length < 1 || value.steps.length > 64) return invalidContract();
|
|
213
|
+
let input = schema2(value.input), result2 = owns(value, "result") ? schema2(value.result) : void 0, ids = /* @__PURE__ */ new Set(), steps = value.steps.map((step) => !record(step) || !fields(step, ["id"], ["summary"]) || typeof step.id != "string" || !stepId.test(step.id) || ids.has(step.id) ? invalidContract() : (ids.add(step.id), { id: step.id, ...owns(step, "summary") ? { summary: schema2(step.summary) } : {} }));
|
|
214
|
+
return { routine: value.routine, input, steps, ...result2 === void 0 ? {} : { result: result2 } };
|
|
215
|
+
}
|
|
216
|
+
function jsonData(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
217
|
+
if (!(value === null || typeof value == "string" || typeof value == "boolean" || typeof value == "number" && Number.isFinite(value))) {
|
|
218
|
+
if (!record(value) && !denseArray(value) || ancestors.has(value)) return invalidValue();
|
|
219
|
+
ancestors.add(value);
|
|
220
|
+
for (let child of Object.values(value)) jsonData(child, ancestors);
|
|
221
|
+
ancestors.delete(value);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
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");
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
check(schema2, value, "$");
|
|
263
|
+
}
|
|
264
|
+
|
|
91
265
|
var InboxContractError = class extends Error {
|
|
92
266
|
constructor(message2) {
|
|
93
267
|
super(message2), this.name = "InboxContractError";
|
|
@@ -157,7 +331,7 @@ function validateInboxFile(value) {
|
|
|
157
331
|
let size = natural(v.size, "file size");
|
|
158
332
|
return size > FILE_ORIGINAL_MAX_BYTES && fail("File exceeds supported size"), { id: fileId, name: name2, mediaType, size, sha256: v.sha256 };
|
|
159
333
|
}
|
|
160
|
-
function
|
|
334
|
+
function fields2(value) {
|
|
161
335
|
let result2 = array(value, "selected fields").map((entry) => {
|
|
162
336
|
let field = object(entry, "field");
|
|
163
337
|
return keys(field, ["label", "value"], "field"), { label: text(field.label, "field label"), value: text(field.value, "field value") };
|
|
@@ -184,7 +358,7 @@ function validateBlock(value) {
|
|
|
184
358
|
label,
|
|
185
359
|
...target,
|
|
186
360
|
...expanded,
|
|
187
|
-
record: { ref: id(r.ref, "record reference"), title: text(r.title, "record title"), fields:
|
|
361
|
+
record: { ref: id(r.ref, "record reference"), title: text(r.title, "record title"), fields: fields2(r.fields) }
|
|
188
362
|
};
|
|
189
363
|
}
|
|
190
364
|
if (b.kind === "records") {
|
|
@@ -194,7 +368,7 @@ function validateBlock(value) {
|
|
|
194
368
|
ref: id(r.ref, "record reference"),
|
|
195
369
|
title: text(r.title, "record title"),
|
|
196
370
|
...r.description === void 0 ? {} : { description: text(r.description, "record description") },
|
|
197
|
-
...r.facts === void 0 ? {} : { facts:
|
|
371
|
+
...r.facts === void 0 ? {} : { facts: fields2(r.facts) },
|
|
198
372
|
...r.view === void 0 ? {} : { view: id(r.view, "row View target") }
|
|
199
373
|
};
|
|
200
374
|
});
|
|
@@ -452,138 +626,6 @@ function createInboxPublisher(transport) {
|
|
|
452
626
|
};
|
|
453
627
|
}
|
|
454
628
|
|
|
455
|
-
var REPOSITORY = /^[a-z0-9][a-z0-9._/-]{0,127}$/;
|
|
456
|
-
function validRepositoryAlias(value) {
|
|
457
|
-
return typeof value == "string" && REPOSITORY.test(value) && !value.includes("//") && !value.includes("..");
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
var RECORD_READ_INPUT_LIMIT = 1024 * 1024, RECORD_CAPTURE_OUTPUT_LIMIT = 25 * 1024 * 1024;
|
|
461
|
-
function validRecordRef(value) {
|
|
462
|
-
return typeof value == "string" && value.length <= 512 && /^[_a-zA-Z0-9][a-zA-Z0-9_-]*(\/[_a-zA-Z0-9][a-zA-Z0-9_-]*)+$/.test(value);
|
|
463
|
-
}
|
|
464
|
-
function validateRecordRead(value) {
|
|
465
|
-
if (!value || typeof value != "object" || Array.isArray(value))
|
|
466
|
-
throw new InboxContractError("Invalid selected-record read");
|
|
467
|
-
let input = value;
|
|
468
|
-
if (Object.keys(input).some((key) => !["repository", "refs", "atCommit"].includes(key)) || !validRepositoryAlias(input.repository) || !Array.isArray(input.refs) || !input.refs.length || input.refs.length > 1e4 || input.refs.some((ref) => !validRecordRef(ref)) || new Set(input.refs).size !== input.refs.length || input.atCommit !== void 0 && (typeof input.atCommit != "string" || !/^[0-9a-f]{40}$/.test(input.atCommit)))
|
|
469
|
-
throw new InboxContractError("Invalid selected-record read");
|
|
470
|
-
return {
|
|
471
|
-
repository: input.repository,
|
|
472
|
-
refs: [...input.refs],
|
|
473
|
-
...input.atCommit === void 0 ? {} : { atCommit: input.atCommit }
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
var AppRoutineContractError = class extends Error {
|
|
478
|
-
constructor(code) {
|
|
479
|
-
super(code);
|
|
480
|
-
this.code = code;
|
|
481
|
-
}
|
|
482
|
-
code;
|
|
483
|
-
}, propertyName = /^[a-z][A-Za-z0-9]*(?![\s\S])/, stepId = /^[a-z0-9]+(?:-[a-z0-9]+)*(?![\s\S])/, routineName = /^[a-z0-9][a-z0-9-]{0,62}(?![\s\S])/;
|
|
484
|
-
function isAppRoutineName(value) {
|
|
485
|
-
return typeof value == "string" && routineName.test(value);
|
|
486
|
-
}
|
|
487
|
-
var owns = (value, key) => Object.hasOwn(value, key), invalidContract = () => {
|
|
488
|
-
throw new AppRoutineContractError("invalid_contract");
|
|
489
|
-
}, invalidValue = () => {
|
|
490
|
-
throw new AppRoutineContractError("invalid_value");
|
|
491
|
-
};
|
|
492
|
-
function record(value) {
|
|
493
|
-
return value !== null && typeof value == "object" && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
|
|
494
|
-
}
|
|
495
|
-
function fields2(value, required, optional = []) {
|
|
496
|
-
return required.every((key) => owns(value, key)) && Object.keys(value).every((key) => required.includes(key) || optional.includes(key));
|
|
497
|
-
}
|
|
498
|
-
function denseArray(value) {
|
|
499
|
-
return Array.isArray(value) && Object.keys(value).length === value.length && Array.from({ length: value.length }, (_, index) => owns(value, String(index))).every(Boolean);
|
|
500
|
-
}
|
|
501
|
-
function parseAppRoutineContract(value) {
|
|
502
|
-
let properties = 0;
|
|
503
|
-
function schema2(value2, depth = 1) {
|
|
504
|
-
if (depth > 8 || !record(value2)) return invalidContract();
|
|
505
|
-
switch (value2.type) {
|
|
506
|
-
case "object": {
|
|
507
|
-
if (!fields2(value2, ["type", "properties", "additionalProperties"], ["required"]) || value2.additionalProperties !== !1 || !record(value2.properties)) return invalidContract();
|
|
508
|
-
let entries = Object.entries(value2.properties);
|
|
509
|
-
if (properties += entries.length, properties > 64 || entries.some(([key]) => !propertyName.test(key))) return invalidContract();
|
|
510
|
-
let required;
|
|
511
|
-
if (owns(value2, "required")) {
|
|
512
|
-
if (!denseArray(value2.required) || value2.required.some((key) => typeof key != "string" || !owns(value2.properties, key)) || new Set(value2.required).size !== value2.required.length) return invalidContract();
|
|
513
|
-
required = [...value2.required];
|
|
514
|
-
}
|
|
515
|
-
return {
|
|
516
|
-
type: "object",
|
|
517
|
-
properties: Object.fromEntries(entries.map(([key, child]) => [key, schema2(child, depth + 1)])),
|
|
518
|
-
...required === void 0 ? {} : { required },
|
|
519
|
-
additionalProperties: !1
|
|
520
|
-
};
|
|
521
|
-
}
|
|
522
|
-
case "array":
|
|
523
|
-
return !fields2(value2, ["type", "items", "maxItems"]) || !Number.isSafeInteger(value2.maxItems) || value2.maxItems < 1 || value2.maxItems > 64 ? invalidContract() : { type: "array", items: schema2(value2.items, depth + 1), maxItems: value2.maxItems };
|
|
524
|
-
case "boolean":
|
|
525
|
-
return fields2(value2, ["type"]) ? { type: value2.type } : invalidContract();
|
|
526
|
-
case "file":
|
|
527
|
-
return !fields2(value2, ["type"], ["storage", "path"]) || owns(value2, "storage") && value2.storage !== "local" && value2.storage !== "remote" || owns(value2, "path") && !safeManagedFilePath(value2.path) ? invalidContract() : {
|
|
528
|
-
type: "file",
|
|
529
|
-
...owns(value2, "storage") ? { storage: value2.storage } : {},
|
|
530
|
-
...owns(value2, "path") ? { path: value2.path } : {}
|
|
531
|
-
};
|
|
532
|
-
case "string":
|
|
533
|
-
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 } : {} };
|
|
534
|
-
case "integer":
|
|
535
|
-
case "number": {
|
|
536
|
-
let validNumber = value2.type === "integer" ? Number.isSafeInteger : Number.isFinite;
|
|
537
|
-
return !fields2(value2, ["type"], ["minimum", "maximum"]) || owns(value2, "minimum") && !validNumber(value2.minimum) || owns(value2, "maximum") && !validNumber(value2.maximum) || owns(value2, "minimum") && owns(value2, "maximum") && value2.minimum > value2.maximum ? invalidContract() : {
|
|
538
|
-
type: value2.type,
|
|
539
|
-
...owns(value2, "minimum") ? { minimum: value2.minimum } : {},
|
|
540
|
-
...owns(value2, "maximum") ? { maximum: value2.maximum } : {}
|
|
541
|
-
};
|
|
542
|
-
}
|
|
543
|
-
default:
|
|
544
|
-
return invalidContract();
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
if (!record(value) || !fields2(value, ["routine", "input", "steps"], ["result"]) || !isAppRoutineName(value.routine) || !denseArray(value.steps) || value.steps.length < 1 || value.steps.length > 64) return invalidContract();
|
|
548
|
-
let input = schema2(value.input), result2 = owns(value, "result") ? schema2(value.result) : void 0, ids = /* @__PURE__ */ new Set(), steps = value.steps.map((step) => !record(step) || !fields2(step, ["id"], ["summary"]) || typeof step.id != "string" || !stepId.test(step.id) || ids.has(step.id) ? invalidContract() : (ids.add(step.id), { id: step.id, ...owns(step, "summary") ? { summary: schema2(step.summary) } : {} }));
|
|
549
|
-
return { routine: value.routine, input, steps, ...result2 === void 0 ? {} : { result: result2 } };
|
|
550
|
-
}
|
|
551
|
-
function jsonData(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
552
|
-
if (!(value === null || typeof value == "string" || typeof value == "boolean" || typeof value == "number" && Number.isFinite(value))) {
|
|
553
|
-
if (!record(value) && !denseArray(value) || ancestors.has(value)) return invalidValue();
|
|
554
|
-
ancestors.add(value);
|
|
555
|
-
for (let child of Object.values(value)) jsonData(child, ancestors);
|
|
556
|
-
ancestors.delete(value);
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
function validateRoutineSchemaValue(schema2, value) {
|
|
560
|
-
switch (schema2.type) {
|
|
561
|
-
case "object":
|
|
562
|
-
if (!record(value) || (schema2.required ?? []).some((key) => !owns(value, key)) || Object.keys(value).some((key) => !owns(schema2.properties, key))) return invalidValue();
|
|
563
|
-
for (let [key, child] of Object.entries(value)) validateRoutineSchemaValue(schema2.properties[key], child);
|
|
564
|
-
return;
|
|
565
|
-
case "array":
|
|
566
|
-
if (!denseArray(value) || value.length > schema2.maxItems) return invalidValue();
|
|
567
|
-
for (let child of value) validateRoutineSchemaValue(schema2.items, child);
|
|
568
|
-
return;
|
|
569
|
-
case "boolean":
|
|
570
|
-
return typeof value != "boolean" ? invalidValue() : void 0;
|
|
571
|
-
case "string":
|
|
572
|
-
return typeof value != "string" || Array.from(value).length > (schema2.maxLength ?? 4096) ? invalidValue() : void 0;
|
|
573
|
-
case "integer":
|
|
574
|
-
case "number":
|
|
575
|
-
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;
|
|
576
|
-
case "file":
|
|
577
|
-
try {
|
|
578
|
-
jsonData(value);
|
|
579
|
-
let file2 = parseFileDescriptor(value);
|
|
580
|
-
if (schema2.path !== void 0 && file2.path !== schema2.path) return invalidValue();
|
|
581
|
-
} catch {
|
|
582
|
-
return invalidValue();
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
|
|
587
629
|
var ROUTINE_DECLARATION_SCHEMA = "https://schemas.oddington.ai/routine/v1.json", ROUTINE_SOURCE_LIMITS = { files: 2e4, bytes: 256 * 1024 * 1024, declarationBytes: 64 * 1024 };
|
|
588
630
|
var invalid = (field) => {
|
|
589
631
|
throw new Error(`Invalid routine declaration: ${field}`);
|
|
@@ -603,11 +645,9 @@ function parseRoutineDeclaration(value) {
|
|
|
603
645
|
(!routineAlias(key) || !["read", "write"].includes(repositories[key])) && invalid(`access.repositories.${key}`), grants[key] = repositories[key];
|
|
604
646
|
access.files !== void 0 && !["read", "read-write"].includes(access.files) && invalid("access.files");
|
|
605
647
|
let runtime = object2(v.runtime === void 0 ? {} : v.runtime, ["features", "resources", "maxDuration"], "runtime");
|
|
606
|
-
runtime.
|
|
607
|
-
let
|
|
608
|
-
|
|
609
|
-
let retry = object2(v.retry === void 0 ? {} : v.retry, ["maxAttempts"], "retry"), maxAttempts = retry.maxAttempts === void 0 ? 1 : retry.maxAttempts;
|
|
610
|
-
(!Number.isInteger(maxAttempts) || maxAttempts < 1 || maxAttempts > 3) && invalid("retry.maxAttempts");
|
|
648
|
+
runtime.features !== void 0 && (!Array.isArray(runtime.features) || runtime.features.length !== 0) && invalid("runtime.features"), runtime.resources !== void 0 && runtime.resources !== "standard" && invalid("runtime.resources"), runtime.maxDuration !== void 0 && runtime.maxDuration !== "15m" && invalid("runtime.maxDuration");
|
|
649
|
+
let retry = object2(v.retry === void 0 ? {} : v.retry, ["maxAttempts"], "retry");
|
|
650
|
+
retry.maxAttempts !== void 0 && retry.maxAttempts !== 1 && invalid("retry.maxAttempts");
|
|
611
651
|
let assets = v.assets === void 0 ? [] : v.assets;
|
|
612
652
|
return (!Array.isArray(assets) || assets.length > 64 || assets.some((item) => !routineAssetPattern(item)) || new Set(assets).size !== assets.length) && invalid("assets"), {
|
|
613
653
|
$schema: ROUTINE_DECLARATION_SCHEMA,
|
|
@@ -618,12 +658,8 @@ function parseRoutineDeclaration(value) {
|
|
|
618
658
|
...access.files === void 0 ? {} : { files: access.files },
|
|
619
659
|
publish: list(access.publish, "access.publish", ["asks", "digests"])
|
|
620
660
|
},
|
|
621
|
-
runtime: {
|
|
622
|
-
|
|
623
|
-
resources: runtime.resources ?? "standard",
|
|
624
|
-
maxDuration: duration
|
|
625
|
-
},
|
|
626
|
-
retry: { maxAttempts },
|
|
661
|
+
runtime: { features: [], resources: "standard", maxDuration: "15m" },
|
|
662
|
+
retry: { maxAttempts: 1 },
|
|
627
663
|
assets: [...assets].sort()
|
|
628
664
|
};
|
|
629
665
|
}
|
|
@@ -672,20 +708,82 @@ function parseRoutineAccess(value) {
|
|
|
672
708
|
}
|
|
673
709
|
}
|
|
674
710
|
|
|
675
|
-
|
|
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;
|
|
676
772
|
var RoutineAIError = class extends Error {
|
|
677
|
-
constructor(code) {
|
|
678
|
-
|
|
773
|
+
constructor(code, options = {}) {
|
|
774
|
+
let detail = code === "invalid_output" ? parseRoutineAIOutputIssue(options.detail) : void 0;
|
|
775
|
+
super(detail ? routineAIOutputMessage(detail) : code);
|
|
679
776
|
this.code = code;
|
|
680
|
-
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 });
|
|
681
778
|
}
|
|
682
779
|
code;
|
|
780
|
+
detail;
|
|
683
781
|
};
|
|
684
|
-
function capture(value, invalid9, oversized) {
|
|
782
|
+
function capture(value, invalid9, oversized, limit = ROUTINE_AI_VALUE_BYTES) {
|
|
685
783
|
try {
|
|
686
784
|
assertRoutineJson(value);
|
|
687
785
|
let encoded = JSON.stringify(value);
|
|
688
|
-
if (new TextEncoder().encode(encoded).byteLength >
|
|
786
|
+
if (new TextEncoder().encode(encoded).byteLength > limit) throw new RoutineAIError(oversized);
|
|
689
787
|
return freezeRoutineMetadata(JSON.parse(encoded));
|
|
690
788
|
} catch (error) {
|
|
691
789
|
throw error instanceof RoutineAIError ? error : new RoutineAIError(invalid9);
|
|
@@ -704,19 +802,47 @@ function parseRoutineAIRequest(value) {
|
|
|
704
802
|
}
|
|
705
803
|
}
|
|
706
804
|
function parseRoutineAIResult(request2, value) {
|
|
707
|
-
let v
|
|
805
|
+
let v;
|
|
708
806
|
try {
|
|
709
|
-
if (
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
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);
|
|
715
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
|
+
});
|
|
716
828
|
} catch {
|
|
717
|
-
throw new RoutineAIError("invalid_output");
|
|
829
|
+
throw new RoutineAIError("invalid_output", { ...text5 === void 0 ? {} : { text: text5 }, detail });
|
|
718
830
|
}
|
|
719
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
|
+
}
|
|
720
846
|
|
|
721
847
|
var LIMIT = 256 * 1024, invalid3 = () => {
|
|
722
848
|
throw new AppRoutineContractError("invalid_value");
|
|
@@ -842,7 +968,7 @@ function captureRoutineInput(definition, input) {
|
|
|
842
968
|
return capture2(contract.input, input === void 0 && Object.hasOwn(contract, "inputDefault") ? contract.inputDefault : input);
|
|
843
969
|
}
|
|
844
970
|
async function executeRoutineDefinition(definition, input, runtime) {
|
|
845
|
-
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) => {
|
|
846
972
|
let operation2 = (async () => {
|
|
847
973
|
if (!active2 || broken || contract.access.ai !== !0) throw new RoutineAIError("access_denied");
|
|
848
974
|
if (!runtime.ai) throw new RoutineAIError("capability_unavailable");
|
|
@@ -861,9 +987,7 @@ async function executeRoutineDefinition(definition, input, runtime) {
|
|
|
861
987
|
if (!active2 || broken) throw new RoutineAIError("access_denied");
|
|
862
988
|
return parseRoutineAIResult(request2, result3);
|
|
863
989
|
})();
|
|
864
|
-
return aiWork.add(operation2), operation2.then(() => aiWork.delete(operation2), (
|
|
865
|
-
aiFailure ??= error, broken = !0, aiWork.delete(operation2);
|
|
866
|
-
}), operation2;
|
|
990
|
+
return aiWork.add(operation2), operation2.then(() => aiWork.delete(operation2), () => aiWork.delete(operation2)), operation2;
|
|
867
991
|
}, called = /* @__PURE__ */ new Set(), reached = /* @__PURE__ */ new Set(), guarded = (value, receiver) => typeof value == "function" ? (...args) => {
|
|
868
992
|
if (!active2 || broken) throw new Error("Run authority ended");
|
|
869
993
|
return guarded(Reflect.apply(value, receiver, args));
|
|
@@ -927,7 +1051,7 @@ async function executeRoutineDefinition(definition, input, runtime) {
|
|
|
927
1051
|
for (accepting = !1, await work; aiWork.size; ) await Promise.allSettled([...aiWork]);
|
|
928
1052
|
active2 = !1;
|
|
929
1053
|
for (let step of contract.steps) reached.has(step.id) || await runtime.step(step.id, { status: "skipped" });
|
|
930
|
-
if (broken) throw failure ??
|
|
1054
|
+
if (broken) throw failure ?? new AppRoutineContractError("invalid_value");
|
|
931
1055
|
if (contract.result !== void 0) return capture2(contract.result, result2);
|
|
932
1056
|
if (result2 !== void 0) return invalid3();
|
|
933
1057
|
}
|
|
@@ -947,6 +1071,11 @@ var DataOperationError = class extends Error {
|
|
|
947
1071
|
component;
|
|
948
1072
|
};
|
|
949
1073
|
|
|
1074
|
+
var RECORD_READ_INPUT_LIMIT = 1024 * 1024, RECORD_CAPTURE_OUTPUT_LIMIT = 25 * 1024 * 1024;
|
|
1075
|
+
function validRecordRef(value) {
|
|
1076
|
+
return typeof value == "string" && value.length <= 512 && /^[_a-zA-Z0-9][a-zA-Z0-9_-]*(\/[_a-zA-Z0-9][a-zA-Z0-9_-]*)+$/.test(value);
|
|
1077
|
+
}
|
|
1078
|
+
|
|
950
1079
|
var invalid4 = () => {
|
|
951
1080
|
throw new DataOperationError("invalid_query");
|
|
952
1081
|
}, historyText = (value) => typeof value == "string" && !/[\u0000\uD800-\uDFFF]/u.test(value);
|
|
@@ -1471,8 +1600,13 @@ async function boundedBytes(stream, limit, signal) {
|
|
|
1471
1600
|
let { done, value } = await reader.read();
|
|
1472
1601
|
if (signal?.aborted) throw unavailable2();
|
|
1473
1602
|
if (done) break;
|
|
1474
|
-
if (!(value instanceof Uint8Array)) throw
|
|
1475
|
-
if (size += value.byteLength, size > limit) throw new FileOperationError(
|
|
1603
|
+
if (!(value instanceof Uint8Array)) throw new FileOperationError("invalid_input", 400, !1, { reason: "invalid_bytes" });
|
|
1604
|
+
if (size += value.byteLength, size > limit) throw new FileOperationError(
|
|
1605
|
+
"request_too_large",
|
|
1606
|
+
413,
|
|
1607
|
+
!1,
|
|
1608
|
+
{ reason: "size_limit", limitBytes: limit, receivedBytes: size, receivedAtLeast: !0 }
|
|
1609
|
+
);
|
|
1476
1610
|
bytes.set(value, size - value.byteLength);
|
|
1477
1611
|
}
|
|
1478
1612
|
} catch (error) {
|
|
@@ -1524,7 +1658,12 @@ async function send(suffix, init2, descriptor, initial, parent) {
|
|
|
1524
1658
|
})();
|
|
1525
1659
|
if (!response2.ok) {
|
|
1526
1660
|
let value2 = await result(response2), error = value2 && typeof value2 == "object" && "error" in value2 ? value2.error : void 0, received = error && typeof error == "object" && "code" in error ? error.code : void 0, code = failures[response2.status]?.find((code2) => code2 === received);
|
|
1527
|
-
throw code ? new FileOperationError(
|
|
1661
|
+
throw code ? new FileOperationError(
|
|
1662
|
+
code,
|
|
1663
|
+
response2.status,
|
|
1664
|
+
code === "capacity" || code === "unavailable",
|
|
1665
|
+
parseFileErrorDetail(error?.detail)
|
|
1666
|
+
) : unavailable2();
|
|
1528
1667
|
}
|
|
1529
1668
|
if (response2.status !== (descriptor === 204 ? 204 : 200) || descriptor === 204 && response2.body !== null)
|
|
1530
1669
|
throw response2.body?.cancel().catch(() => {
|
|
@@ -1588,7 +1727,7 @@ async function multipartCreate(input, metadata, exchange, release = () => {
|
|
|
1588
1727
|
};
|
|
1589
1728
|
for (let sent = 0, partNumber = 1; sent < metadata.size || metadata.size === 0 && partNumber === 1; partNumber++) {
|
|
1590
1729
|
let length = Math.min(PART_BYTES, metadata.size - sent), part;
|
|
1591
|
-
if (input.file instanceof Uint8Array) part = input.file.subarray(sent, sent + length)
|
|
1730
|
+
if (input.file instanceof Uint8Array) part = new Uint8Array(input.file.subarray(sent, sent + length));
|
|
1592
1731
|
else if (input.file instanceof Blob) {
|
|
1593
1732
|
let slice = input.file.slice(sent, sent + length);
|
|
1594
1733
|
part = new Uint8Array(await request2(() => slice.arrayBuffer()));
|
|
@@ -1717,7 +1856,13 @@ var files = {
|
|
|
1717
1856
|
let blob = input.file instanceof Blob, array3 = input.file instanceof Uint8Array, stream = input.file instanceof ReadableStream;
|
|
1718
1857
|
if (!blob && !array3 && !stream) throw invalid7();
|
|
1719
1858
|
let intrinsic = blob ? input.file.size : array3 ? input.file.byteLength : void 0;
|
|
1720
|
-
if (intrinsic !== void 0 && input.size !== void 0 && input.size !== intrinsic)
|
|
1859
|
+
if (intrinsic !== void 0 && input.size !== void 0 && input.size !== intrinsic)
|
|
1860
|
+
throw !Number.isSafeInteger(input.size) || input.size < 0 ? new FileOperationError("invalid_input", 400, !1, { reason: "invalid_size" }) : new FileOperationError(
|
|
1861
|
+
"invalid_input",
|
|
1862
|
+
400,
|
|
1863
|
+
!1,
|
|
1864
|
+
{ reason: "size_mismatch", expectedBytes: input.size, receivedBytes: intrinsic }
|
|
1865
|
+
);
|
|
1721
1866
|
let name2 = input.name;
|
|
1722
1867
|
name2 === void 0 && blob && "name" in input.file && typeof input.file.name == "string" && (name2 = typeof globalThis.File < "u" && input.file instanceof globalThis.File ? input.file.name : input.file.name.split(/[\\/]/).at(-1));
|
|
1723
1868
|
let mediaType = declaredOriginalMediaType(input.mediaType === void 0 ? blob && input.file.type ? input.file.type : "application/octet-stream" : input.mediaType), metadata = parseFileCreateMetadata({
|
|
@@ -1754,12 +1899,17 @@ var files = {
|
|
|
1754
1899
|
}
|
|
1755
1900
|
return metadata.size > MAX_BYTES ? multipartCreate(input, metadata, (suffix, init2, kind, signal) => kind === !0 ? send(suffix, init2, !0, context, signal) : send(suffix, init2, "control", context, signal)) : timed(async (signal) => {
|
|
1756
1901
|
try {
|
|
1757
|
-
let bytes = array3 ? input.file
|
|
1902
|
+
let bytes = array3 ? new Uint8Array(input.file) : await boundedBytes(
|
|
1758
1903
|
blob ? input.file.stream() : input.file,
|
|
1759
1904
|
metadata.size,
|
|
1760
1905
|
signal
|
|
1761
1906
|
);
|
|
1762
|
-
if (bytes.byteLength !== metadata.size) throw
|
|
1907
|
+
if (bytes.byteLength !== metadata.size) throw new FileOperationError(
|
|
1908
|
+
"invalid_input",
|
|
1909
|
+
400,
|
|
1910
|
+
!1,
|
|
1911
|
+
{ reason: "size_mismatch", expectedBytes: metadata.size, receivedBytes: bytes.byteLength }
|
|
1912
|
+
);
|
|
1763
1913
|
if (signal.aborted) throw unavailable2();
|
|
1764
1914
|
return await send("", {
|
|
1765
1915
|
method: "POST",
|
|
@@ -1785,7 +1935,7 @@ var files = {
|
|
|
1785
1935
|
let buffer = new Uint8Array(Math.min(MAX_BYTES, input.content.length * 3)), encoded = new TextEncoder().encodeInto(input.content, buffer);
|
|
1786
1936
|
if (encoded.read !== input.content.length) throw new FileOperationError("request_too_large", 413);
|
|
1787
1937
|
bytes = buffer.slice(0, encoded.written);
|
|
1788
|
-
} else bytes = input.content
|
|
1938
|
+
} else bytes = new Uint8Array(input.content);
|
|
1789
1939
|
let metadata = parseFileEnrichMetadata({
|
|
1790
1940
|
name: input.name,
|
|
1791
1941
|
size: bytes.byteLength,
|
|
@@ -2130,68 +2280,6 @@ var connections = /* @__PURE__ */ runtimeConnections();
|
|
|
2130
2280
|
|
|
2131
2281
|
var LOCAL_ROUTINE_VALUE_BYTES = 256 * 1024;
|
|
2132
2282
|
|
|
2133
|
-
var RECORD_WRITE_INPUT_LIMIT = 8 * 1024 * 1024, RoutineRecordWriteError = class extends Error {
|
|
2134
|
-
constructor(message2, effect) {
|
|
2135
|
-
super(message2);
|
|
2136
|
-
this.effect = effect;
|
|
2137
|
-
this.name = "RoutineRecordWriteError";
|
|
2138
|
-
}
|
|
2139
|
-
effect;
|
|
2140
|
-
};
|
|
2141
|
-
function validateRecordWrite(value) {
|
|
2142
|
-
let input = serializable(value);
|
|
2143
|
-
if (!input || Array.isArray(input) || typeof input != "object" || Object.keys(input).sort().join(",") !== "records,repository" || !validRepositoryAlias(input.repository) || !Array.isArray(input.records) || !input.records.length || input.records.length > 1e4 || new Set(input.records.map((edit) => edit?.ref)).size !== input.records.length)
|
|
2144
|
-
throw new InboxContractError("Invalid record update batch");
|
|
2145
|
-
for (let edit of input.records) {
|
|
2146
|
-
if (!edit || Array.isArray(edit) || typeof edit != "object" || !validRecordRef(edit.ref))
|
|
2147
|
-
throw new InboxContractError("Invalid record edit");
|
|
2148
|
-
if ("expectedVersion" in edit) {
|
|
2149
|
-
if (Object.keys(edit).sort().join(",") !== "expectedVersion,ref,set" || typeof edit.expectedVersion != "string" || !/^record-v1:[0-9a-f]{64}$/.test(edit.expectedVersion) || !edit.set || Array.isArray(edit.set) || typeof edit.set != "object" || !Object.keys(edit.set).length)
|
|
2150
|
-
throw new InboxContractError("Invalid record edit");
|
|
2151
|
-
continue;
|
|
2152
|
-
}
|
|
2153
|
-
if (Object.keys(edit).sort().join(",") !== "fields,path,ref" || typeof edit.path != "string" || !validCreatePath(edit.path) || !edit.fields || Array.isArray(edit.fields) || typeof edit.fields != "object" || !Object.keys(edit.fields).length)
|
|
2154
|
-
throw new InboxContractError("Invalid record creation");
|
|
2155
|
-
}
|
|
2156
|
-
return structuredClone(input);
|
|
2157
|
-
}
|
|
2158
|
-
function validateRecordUpdate(value) {
|
|
2159
|
-
let input = validateRecordWrite(value);
|
|
2160
|
-
if (input.records.some((record4) => !("expectedVersion" in record4)))
|
|
2161
|
-
throw new InboxContractError("Record creation is unavailable for this operation");
|
|
2162
|
-
return input;
|
|
2163
|
-
}
|
|
2164
|
-
function validateRecordDelete(value) {
|
|
2165
|
-
let input = serializable(value);
|
|
2166
|
-
if (!input || Array.isArray(input) || typeof input != "object" || Object.keys(input).sort().join(",") !== "records,repository" || !validRepositoryAlias(input.repository) || !Array.isArray(input.records) || !input.records.length || input.records.length > 1e4 || new Set(input.records.map((record4) => record4?.ref)).size !== input.records.length)
|
|
2167
|
-
throw new InboxContractError("Invalid record deletion batch");
|
|
2168
|
-
for (let record4 of input.records)
|
|
2169
|
-
if (!record4 || Array.isArray(record4) || typeof record4 != "object" || Object.keys(record4).sort().join(",") !== "expectedVersion,ref" || !validRecordRef(record4.ref) || typeof record4.expectedVersion != "string" || !/^record-v1:[0-9a-f]{64}$/.test(record4.expectedVersion))
|
|
2170
|
-
throw new InboxContractError("Invalid record deletion");
|
|
2171
|
-
return structuredClone(input);
|
|
2172
|
-
}
|
|
2173
|
-
function validateRecordChange(value) {
|
|
2174
|
-
let input = serializable(value);
|
|
2175
|
-
if (!input || Array.isArray(input) || typeof input != "object" || Object.keys(input).sort().join(",") !== "records,repository" || !validRepositoryAlias(input.repository) || !Array.isArray(input.records) || !input.records.length || input.records.length > 1e4 || new Set(input.records.map((record4) => record4?.ref)).size !== input.records.length || new TextEncoder().encode(JSON.stringify(input)).byteLength > RECORD_WRITE_INPUT_LIMIT)
|
|
2176
|
-
throw new InboxContractError("Invalid record change batch");
|
|
2177
|
-
let writes = [], deletes = [];
|
|
2178
|
-
for (let record4 of input.records) {
|
|
2179
|
-
if (!record4 || typeof record4 != "object" || Array.isArray(record4))
|
|
2180
|
-
throw new InboxContractError("Invalid record change");
|
|
2181
|
-
"set" in record4 || "fields" in record4 ? writes.push(record4) : deletes.push(record4);
|
|
2182
|
-
}
|
|
2183
|
-
return writes.length && validateRecordWrite({ repository: input.repository, records: writes }), deletes.length && validateRecordDelete({ repository: input.repository, records: deletes }), structuredClone(input);
|
|
2184
|
-
}
|
|
2185
|
-
function validateRoutineRecordWrite(value) {
|
|
2186
|
-
let body = serializable(value);
|
|
2187
|
-
if (!body || typeof body != "object" || Array.isArray(body) || Object.keys(body).sort().join(",") !== "input,requestKey" || typeof body.requestKey != "string" || !body.requestKey.trim() || body.requestKey !== body.requestKey.trim() || body.requestKey.length > 512)
|
|
2188
|
-
throw new InboxContractError("A stable routine request key is required");
|
|
2189
|
-
return { input: validateRecordChange(body.input), requestKey: body.requestKey };
|
|
2190
|
-
}
|
|
2191
|
-
function validCreatePath(value) {
|
|
2192
|
-
return value === value.trim() && value.endsWith(".md") && !value.startsWith("/") && value.split("/").every((segment) => !!segment && ![".", ".."].includes(segment) && /^[a-zA-Z0-9_.-]+$/.test(segment));
|
|
2193
|
-
}
|
|
2194
|
-
|
|
2195
2283
|
function locationIdentity(location) {
|
|
2196
2284
|
let coordinates = location.provider === "os" ? [location.file] : location.provider === "gmail" ? [location.connection, location.message, location.attachment] : location.provider === "dropbox" ? [location.connection, location.file] : void 0;
|
|
2197
2285
|
if (coordinates?.every((value) => typeof value == "string" && value.length > 0))
|
|
@@ -3008,7 +3096,15 @@ function validPair(value) {
|
|
|
3008
3096
|
return validProof(pair.credential) && validProof(pair.renewalCredential) && pair.credential !== pair.renewalCredential && validBinding(pair.context);
|
|
3009
3097
|
}
|
|
3010
3098
|
|
|
3011
|
-
var
|
|
3099
|
+
var WorkspaceAdmissionError = class extends Error {
|
|
3100
|
+
constructor(status) {
|
|
3101
|
+
super("Workspace admission unavailable");
|
|
3102
|
+
this.status = status;
|
|
3103
|
+
}
|
|
3104
|
+
status;
|
|
3105
|
+
};
|
|
3106
|
+
|
|
3107
|
+
var unavailable3 = () => new InboxOperationError(503, "unavailable"), stale = () => new InboxOperationError(401, "authority_unavailable"), contextFailure = (error) => error instanceof WorkspaceAdmissionError ? new InboxOperationError(error.status, error.status === 401 ? "authority_unavailable" : error.status === 403 ? "access_denied" : "unavailable") : stale();
|
|
3012
3108
|
function hostFailure(error, context) {
|
|
3013
3109
|
return !context.isCurrent() || context.expiresAt <= Date.now() || error instanceof InboxContextError && error.status === 401 ? stale() : error instanceof InboxContextError && error.status === 403 ? new InboxOperationError(403, "access_denied") : unavailable3();
|
|
3014
3110
|
}
|
|
@@ -3016,8 +3112,8 @@ async function send2(operation2, input, admitted) {
|
|
|
3016
3112
|
let envelope = parseInboxRequest({ operation: operation2, input }), context;
|
|
3017
3113
|
try {
|
|
3018
3114
|
context = admitted ?? await resolveAppContext();
|
|
3019
|
-
} catch {
|
|
3020
|
-
throw
|
|
3115
|
+
} catch (error) {
|
|
3116
|
+
throw contextFailure(error);
|
|
3021
3117
|
}
|
|
3022
3118
|
if (context.kind === "local") {
|
|
3023
3119
|
if (!isAppQuestionOperation(operation2)) throw new InboxOperationError(403, "access_denied");
|
|
@@ -3125,8 +3221,8 @@ var inbox = {
|
|
|
3125
3221
|
let items = [], beforeNumber, initial;
|
|
3126
3222
|
try {
|
|
3127
3223
|
initial = await resolveAppContext();
|
|
3128
|
-
} catch {
|
|
3129
|
-
throw
|
|
3224
|
+
} catch (error) {
|
|
3225
|
+
throw contextFailure(error);
|
|
3130
3226
|
}
|
|
3131
3227
|
let host;
|
|
3132
3228
|
try {
|
|
@@ -3164,8 +3260,8 @@ var inbox = {
|
|
|
3164
3260
|
let routines = Object.freeze(supplied.routines), questions = [], beforeNumber, initial;
|
|
3165
3261
|
try {
|
|
3166
3262
|
initial = await resolveAppContext();
|
|
3167
|
-
} catch {
|
|
3168
|
-
throw
|
|
3263
|
+
} catch (error) {
|
|
3264
|
+
throw contextFailure(error);
|
|
3169
3265
|
}
|
|
3170
3266
|
let check = () => {
|
|
3171
3267
|
if (!initial.isCurrent() || initial.expiresAt <= Date.now()) throw stale();
|
|
@@ -3215,14 +3311,15 @@ function init({ auth: input }) {
|
|
|
3215
3311
|
}
|
|
3216
3312
|
let active2 = !0, revision = 0, person, token, binding = {}, local2 = localOrigin ? appContext(localOrigin, () => active2) : void 0, subscription, stopDisposal, resolve = async () => {
|
|
3217
3313
|
if (local2) return local2.resolve();
|
|
3218
|
-
if (!active2 || !endpoint || !workspace || globalThis.location.origin !== endpoint) throw new Error("Hosted Data context unavailable.");
|
|
3314
|
+
if (registered && await registered.ready(), !active2 || !endpoint || !workspace || globalThis.location.origin !== endpoint) throw new Error("Hosted Data context unavailable.");
|
|
3219
3315
|
let started = revision, { data: { session }, error } = await auth2.getSession();
|
|
3220
3316
|
if (!active2 || globalThis.location.origin !== endpoint || started !== revision || error || !session || person !== void 0 && person !== session.user.id)
|
|
3221
3317
|
throw new Error("Hosted Data context unavailable.");
|
|
3222
3318
|
let expiresAt = (session.expires_at ?? 0) * 1e3;
|
|
3223
3319
|
if (!Number.isFinite(expiresAt) || expiresAt <= Date.now() || !session.user.id)
|
|
3224
3320
|
throw new Error("Hosted Data context unavailable.");
|
|
3225
|
-
person = session.user.id
|
|
3321
|
+
if (person = session.user.id, registered && await registered.admitWorkspace(), !active2 || globalThis.location.origin !== endpoint || started !== revision || person !== session.user.id)
|
|
3322
|
+
throw new Error("Hosted Data context unavailable.");
|
|
3226
3323
|
let isCurrent = () => active2 && globalThis.location.origin === endpoint && revision === started && person === session.user.id;
|
|
3227
3324
|
return {
|
|
3228
3325
|
workspace,
|
|
@@ -3369,6 +3466,36 @@ function createBrowserAuth(runtime) {
|
|
|
3369
3466
|
function current2(c, origin, revision) {
|
|
3370
3467
|
return c.session && !validSession(c.session) && expire(c), c.active && c.revision === revision && globalThis.location?.origin === origin;
|
|
3371
3468
|
}
|
|
3469
|
+
async function admitWorkspace(c, origin) {
|
|
3470
|
+
if (origin === "https://auth.oddington.ai" || origin === "http://127.0.0.1:5176") return;
|
|
3471
|
+
let revision = c.revision;
|
|
3472
|
+
if (!current2(c, origin, revision) || !c.session || c.state.pending === "sign_out") throw runtime.failure("stale");
|
|
3473
|
+
if (c.admission?.revision !== revision) {
|
|
3474
|
+
let token = c.session.access_token, result2 = (async () => {
|
|
3475
|
+
let status;
|
|
3476
|
+
try {
|
|
3477
|
+
let response2 = await fetch(`${origin}/api/auth`, {
|
|
3478
|
+
method: "POST",
|
|
3479
|
+
headers: { authorization: `Bearer ${token}` },
|
|
3480
|
+
credentials: "same-origin",
|
|
3481
|
+
redirect: "error",
|
|
3482
|
+
cache: "no-store",
|
|
3483
|
+
signal: AbortSignal.timeout(15e3)
|
|
3484
|
+
});
|
|
3485
|
+
status = response2.status;
|
|
3486
|
+
let value = status === 200 ? await response2.json() : null;
|
|
3487
|
+
status === 200 && value?.admitted !== !0 && (status = 503), status !== 200 && response2.body?.cancel().catch(() => {
|
|
3488
|
+
});
|
|
3489
|
+
} catch {
|
|
3490
|
+
status = 503;
|
|
3491
|
+
}
|
|
3492
|
+
if (!current2(c, origin, revision) || c.state.pending === "sign_out") throw runtime.failure("stale");
|
|
3493
|
+
if (status !== 200) throw new WorkspaceAdmissionError(status === 401 ? 401 : status === 403 ? 403 : 503);
|
|
3494
|
+
})();
|
|
3495
|
+
c.admission = { revision, result: result2 };
|
|
3496
|
+
}
|
|
3497
|
+
if (await c.admission.result, !current2(c, origin, revision)) throw runtime.failure("stale");
|
|
3498
|
+
}
|
|
3372
3499
|
return { create() {
|
|
3373
3500
|
let origin = globalThis.location?.origin ?? "", c = coordinators.get(origin);
|
|
3374
3501
|
c || (c = createCoordinator(origin), coordinators.set(origin, c));
|
|
@@ -3458,7 +3585,9 @@ function createBrowserAuth(runtime) {
|
|
|
3458
3585
|
async sendCode(email) {
|
|
3459
3586
|
if (assertActive(), !validEmail(email)) throw runtime.failure("invalid_input");
|
|
3460
3587
|
await action("send_code", async (provider2) => {
|
|
3461
|
-
let { error } = await provider2.signInWithOtp({ email: email.trim(), options: {
|
|
3588
|
+
let { error } = await provider2.signInWithOtp({ email: email.trim(), options: {
|
|
3589
|
+
shouldCreateUser: origin !== "https://auth.oddington.ai" && origin !== "http://127.0.0.1:5176"
|
|
3590
|
+
} });
|
|
3462
3591
|
if (error && !accountRefusal(error)) throw runtime.failure("unavailable");
|
|
3463
3592
|
});
|
|
3464
3593
|
},
|
|
@@ -3470,6 +3599,11 @@ function createBrowserAuth(runtime) {
|
|
|
3470
3599
|
if (!data3.session || !validSession(data3.session)) throw runtime.failure("invalid_code");
|
|
3471
3600
|
if (!current2(coordinator, origin, revision) && (coordinator.session?.access_token !== data3.session.access_token || coordinator.revision !== revision + 1 || coordinator.state.pending !== "verify_code")) throw runtime.failure("stale");
|
|
3472
3601
|
accept(coordinator, "SIGNED_IN", data3.session);
|
|
3602
|
+
try {
|
|
3603
|
+
await admitWorkspace(coordinator, origin);
|
|
3604
|
+
} catch (error2) {
|
|
3605
|
+
if (!(error2 instanceof WorkspaceAdmissionError)) throw error2;
|
|
3606
|
+
}
|
|
3473
3607
|
});
|
|
3474
3608
|
},
|
|
3475
3609
|
async signOut() {
|
|
@@ -3495,6 +3629,12 @@ function createBrowserAuth(runtime) {
|
|
|
3495
3629
|
}
|
|
3496
3630
|
});
|
|
3497
3631
|
return registerBrowserAuth(handle2, {
|
|
3632
|
+
async ready() {
|
|
3633
|
+
assertActive(), await coordinator.ready, assertActive();
|
|
3634
|
+
},
|
|
3635
|
+
async admitWorkspace() {
|
|
3636
|
+
assertActive(), await admitWorkspace(coordinator, origin), assertActive();
|
|
3637
|
+
},
|
|
3498
3638
|
onDispose(callback) {
|
|
3499
3639
|
return active2 ? (disposalObservers.add(callback), () => {
|
|
3500
3640
|
disposalObservers.delete(callback);
|
|
@@ -3926,18 +4066,11 @@ function validateFileAddOptions(value) {
|
|
|
3926
4066
|
return v;
|
|
3927
4067
|
}
|
|
3928
4068
|
|
|
3929
|
-
var PREPARED_RECORD_INPUT_LIMIT = 64 * 1024 * 1024;
|
|
3930
|
-
|
|
3931
|
-
function routineDataEnvelope(command) {
|
|
3932
|
-
return "apply" in command ? { operation: "changes.publish", captured: command.apply } : { operation: "changes.publish", request: command };
|
|
3933
|
-
}
|
|
3934
|
-
|
|
3935
4069
|
function routinePublicationClient2(options = {}) {
|
|
3936
4070
|
let environment = {}, installed2 = routineTransport(), local2 = installed2?.request ?? void 0, workspace = installed2 ? "installed" : options.workspace ?? environment.OS_ROUTINE_WORKSPACE, capability = options.capability ?? environment.OS_ROUTINE_RUN;
|
|
3937
4071
|
if (!workspace || !/^[a-z0-9][a-z0-9-]*$/.test(workspace) || capability !== void 0 && !/^[0-9a-f]{64}$/.test(capability))
|
|
3938
4072
|
throw new Error("A workspace and valid optional routine capability are required");
|
|
3939
4073
|
let workingRoot = options.workingRoot ?? environment.OS_INBOX_WORKING_ROOT ?? void 0, headers = capability ? { "x-os-run": capability } : {}, request2 = options.request ?? (capability ? async (path, init2) => {
|
|
3940
|
-
if (path === "/api/data") return fetch(`https://${workspace}.oddington.ai/api/data`, { ...init2, redirect: "error" });
|
|
3941
4074
|
let origin = `https://${workspace}.oddington.ai`, target = new URL(path, `${origin}/`);
|
|
3942
4075
|
if (workspace.length > 63 || ["auth", "www", "api", "os"].includes(workspace) || target.origin !== origin || target.search || target.hash || target.username || target.password || !/^\/api\/inbox\/(check|publish|resolve|files)$/.test(path) || init2?.method !== "POST")
|
|
3943
4076
|
throw new Error("Invalid routine endpoint");
|
|
@@ -3947,49 +4080,23 @@ function routinePublicationClient2(options = {}) {
|
|
|
3947
4080
|
});
|
|
3948
4081
|
async function send3(operation2, body) {
|
|
3949
4082
|
if (local2) return await local2(operation2, body);
|
|
3950
|
-
let
|
|
4083
|
+
let response2 = await request2(`/api/inbox/${operation2}`, {
|
|
3951
4084
|
method: "POST",
|
|
3952
|
-
headers: {
|
|
3953
|
-
|
|
3954
|
-
...headers,
|
|
3955
|
-
...writing || reading ? { "x-os-workspace": workspace, "x-os-captured-records": "1" } : {}
|
|
3956
|
-
},
|
|
3957
|
-
body: JSON.stringify(writing ? routineDataEnvelope(validateRoutineRecordWrite(body)) : reading ? { operation: "records.select", request: validateRecordRead(body) } : body)
|
|
4085
|
+
headers: { "content-type": "application/json", ...headers },
|
|
4086
|
+
body: JSON.stringify(body)
|
|
3958
4087
|
}), result2 = await response2.json();
|
|
3959
|
-
if (!response2.ok && operation2 === "records/update")
|
|
3960
|
-
throw new RoutineRecordWriteError(result2.error ?? "Could not confirm routine preparation", result2.effect ?? "uncertain");
|
|
3961
4088
|
if (!response2.ok) throw new Error(result2.error ?? "Could not confirm routine publication");
|
|
3962
4089
|
return result2;
|
|
3963
4090
|
}
|
|
3964
|
-
let transport = {
|
|
3965
|
-
async publish(body) {
|
|
3966
|
-
return await send3("publish", body);
|
|
3967
|
-
},
|
|
3968
|
-
async resolve(body) {
|
|
3969
|
-
await send3("resolve", body);
|
|
3970
|
-
}
|
|
3971
|
-
};
|
|
3972
|
-
async function readRecords(input) {
|
|
3973
|
-
return await send3("records/read", validateRecordRead(input));
|
|
3974
|
-
}
|
|
3975
4091
|
return {
|
|
3976
|
-
...createInboxPublisher(
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
readRecords,
|
|
3980
|
-
async writeRecords(input, options2) {
|
|
3981
|
-
if (!capability && !installed2) throw new Error("A trusted routine launch is required");
|
|
3982
|
-
return await send3("records/update", validateRoutineRecordWrite({ input, requestKey: options2?.requestKey }));
|
|
3983
|
-
},
|
|
3984
|
-
async updateRecords(input, options2) {
|
|
3985
|
-
if (!capability && !installed2) throw new Error("A trusted routine launch is required");
|
|
3986
|
-
return await send3("records/update", validateRoutineRecordWrite({ input: validateRecordUpdate(input), requestKey: options2?.requestKey }));
|
|
4092
|
+
...createInboxPublisher({
|
|
4093
|
+
async publish(body) {
|
|
4094
|
+
return await send3("publish", body);
|
|
3987
4095
|
},
|
|
3988
|
-
async
|
|
3989
|
-
|
|
3990
|
-
return await send3("records/update", validateRoutineRecordWrite({ input: validateRecordDelete(input), requestKey: options2?.requestKey }));
|
|
4096
|
+
async resolve(body) {
|
|
4097
|
+
await send3("resolve", body);
|
|
3991
4098
|
}
|
|
3992
|
-
},
|
|
4099
|
+
}),
|
|
3993
4100
|
files: {
|
|
3994
4101
|
async add(value) {
|
|
3995
4102
|
let input = validateFileAddOptions(value);
|
|
@@ -4010,7 +4117,7 @@ export {
|
|
|
4010
4117
|
FileOperationError,
|
|
4011
4118
|
InboxContractError,
|
|
4012
4119
|
InboxOperationError,
|
|
4013
|
-
|
|
4120
|
+
RoutineAIError,
|
|
4014
4121
|
app,
|
|
4015
4122
|
asksBlock,
|
|
4016
4123
|
auth,
|