@oddington/sdk 0.1.25 → 0.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -40,7 +40,7 @@ import { defineRoutine, file, schema } from '@oddington/sdk';
40
40
 
41
41
  export default defineRoutine({
42
42
  id: 'invoice-import',
43
- input: schema.object({ document: file() }),
43
+ input: schema.object({ document: file({ storage: 'local', path: 'Statements' }) }),
44
44
  access: { files: 'read' },
45
45
  steps: {
46
46
  read: {
@@ -67,8 +67,10 @@ skipped. Omitted input normalizes to `{}`. A top-level schema can supply a valid
67
67
  immutable default with `.default(value)`, such as
68
68
  `schema.object({ files: schema.array(file(), { maxItems: 32 }) }).default({ files: [] })`.
69
69
  Nested defaults are unsupported and rejected. Omitted steps permit no step call;
70
- omitted result permits no returned value. `file()` declares a complete OS File descriptor;
71
- the trusted launcher separately verifies current File-use authority. The supplied
70
+ omitted result permits no returned value. `file()` declares a complete OS File descriptor.
71
+ Its optional `storage` selects local or remote placement and its optional `path`
72
+ selects a managed OS parent such as `Statements`; it is never a machine or provider
73
+ path. The trusted launcher separately verifies current File-use authority. The supplied
72
74
  runtime contains `run.id`, `data`, `files`, `connections`, `inbox`, and `step`.
73
75
  Accepted input and saved values are immutable snapshots. Access declarations set
74
76
  maximum named capabilities and grant no authority; the trusted host checks current
package/dist/browser.js CHANGED
@@ -459,6 +459,9 @@ function safeFileName(value) {
459
459
  function safeFilePath(value) {
460
460
  return typeof value == "string" && value.length <= 1024 && value.split("/").every(safeFileName);
461
461
  }
462
+ function safeManagedFilePath(value) {
463
+ return safeFilePath(value) && value.split("/")[0].toLowerCase() !== ".os";
464
+ }
462
465
  var ORIGINAL_MEDIA_TYPES = ["application/pdf", "text/plain", "text/markdown", "application/json", "image/png", "image/jpeg"];
463
466
  function declaredOriginalMediaType(value) {
464
467
  let normalized = value.split(";", 1)[0].trim().toLowerCase();
@@ -472,7 +475,7 @@ function parseFileCreateMetadata(value, maxBytes) {
472
475
  let input = value;
473
476
  if (Object.keys(input).some((key) => !["name", "path", "storage", "csv", "inputMediaType", "size", "requestKey"].includes(key))) return invalid8();
474
477
  let path = input.path === void 0 ? "Files" : input.path;
475
- if (!safeFileName(input.name) || !safeFilePath(path) || path.split("/")[0].toLowerCase() === ".os" || input.storage !== void 0 && input.storage !== "remote" && input.storage !== "local" || input.inputMediaType !== void 0 && (typeof input.inputMediaType != "string" || !ORIGINAL_MEDIA_TYPES.includes(input.inputMediaType)) || !Number.isSafeInteger(input.size) || input.size < 0) return invalid8();
478
+ if (!safeFileName(input.name) || !safeManagedFilePath(path) || input.storage !== void 0 && input.storage !== "remote" && input.storage !== "local" || input.inputMediaType !== void 0 && (typeof input.inputMediaType != "string" || !ORIGINAL_MEDIA_TYPES.includes(input.inputMediaType)) || !Number.isSafeInteger(input.size) || input.size < 0) return invalid8();
476
479
  if (input.size > maxBytes) throw new FileOperationError("request_too_large", 413);
477
480
  if (typeof input.requestKey != "string" || input.requestKey.length < 1 || input.requestKey.length > 256 || input.requestKey !== input.requestKey.trim() || /[\u0000-\u001f\u007f]/.test(input.requestKey)) return invalid8();
478
481
  let csv;
@@ -545,7 +548,11 @@ function parseAppRoutineContract(value) {
545
548
  case "boolean":
546
549
  return fields2(value2, ["type"]) ? { type: value2.type } : invalidContract();
547
550
  case "file":
548
- return !fields2(value2, ["type"], ["storage"]) || owns(value2, "storage") && value2.storage !== "local" && value2.storage !== "remote" ? invalidContract() : { type: "file", ...owns(value2, "storage") ? { storage: value2.storage } : {} };
551
+ return !fields2(value2, ["type"], ["storage", "path"]) || owns(value2, "storage") && value2.storage !== "local" && value2.storage !== "remote" || owns(value2, "path") && !safeManagedFilePath(value2.path) ? invalidContract() : {
552
+ type: "file",
553
+ ...owns(value2, "storage") ? { storage: value2.storage } : {},
554
+ ...owns(value2, "path") ? { path: value2.path } : {}
555
+ };
549
556
  case "string":
550
557
  return !fields2(value2, ["type"], ["maxLength"]) || owns(value2, "maxLength") && (!Number.isSafeInteger(value2.maxLength) || value2.maxLength < 0 || value2.maxLength > 4096) ? invalidContract() : { type: "string", ...owns(value2, "maxLength") ? { maxLength: value2.maxLength } : {} };
551
558
  case "integer":
@@ -592,7 +599,9 @@ function validateRoutineSchemaValue(schema2, value) {
592
599
  return typeof value != "number" || !(schema2.type === "integer" ? Number.isSafeInteger(value) : Number.isFinite(value)) || schema2.minimum !== void 0 && value < schema2.minimum || schema2.maximum !== void 0 && value > schema2.maximum ? invalidValue() : void 0;
593
600
  case "file":
594
601
  try {
595
- jsonData(value), parseFileDescriptor(value);
602
+ jsonData(value);
603
+ let file2 = parseFileDescriptor(value);
604
+ if (schema2.path !== void 0 && file2.path !== schema2.path) return invalidValue();
596
605
  } catch {
597
606
  return invalidValue();
598
607
  }
@@ -791,7 +800,7 @@ var schema = {
791
800
  boolean() {
792
801
  return declaration({ type: "boolean" });
793
802
  }
794
- }, file = (options = {}) => (assertRoutineFields(options, ["storage"]), declaration({ type: "file", ...options })), definitions = /* @__PURE__ */ new WeakMap(), defineRoutine = (definition) => {
803
+ }, file = (options = {}) => (assertRoutineFields(options, ["storage", "path"]), declaration({ type: "file", ...options })), definitions = /* @__PURE__ */ new WeakMap(), defineRoutine = (definition) => {
795
804
  if (assertRoutineFields(definition, ["id", "title", "input", "steps", "result", "access", "responses", "run"]), !isAppRoutineName(definition.id) || definition.title !== void 0 && !routineTitle(definition.title) || typeof definition.run != "function") return invalidContract3();
796
805
  let input = definition.input === void 0 ? { schema: empty, default: {} } : schemaMetadata(definition.input), result2 = definition.result === void 0 ? void 0 : schemaMetadata(definition.result).schema, access = parseRoutineAccess(definition.access);
797
806
  assertRoutineFields(definition.steps ?? {});
@@ -2957,10 +2966,10 @@ var unavailable3 = () => new InboxOperationError(503, "unavailable"), stale = ()
2957
2966
  function hostFailure(error, context2) {
2958
2967
  return !context2.isCurrent() || context2.expiresAt <= Date.now() || error instanceof InboxContextError && error.status === 401 ? stale() : error instanceof InboxContextError && error.status === 403 ? new InboxOperationError(403, "access_denied") : unavailable3();
2959
2968
  }
2960
- async function send2(operation2, input) {
2969
+ async function send2(operation2, input, admitted) {
2961
2970
  let envelope = parseInboxRequest({ operation: operation2, input }), context2;
2962
2971
  try {
2963
- context2 = await resolveAppContext();
2972
+ context2 = admitted ?? await resolveAppContext();
2964
2973
  } catch {
2965
2974
  throw stale();
2966
2975
  }
@@ -3120,7 +3129,7 @@ var inbox = {
3120
3129
  let page = await send2("questions.list", {
3121
3130
  routines,
3122
3131
  ...beforeNumber === void 0 ? {} : { beforeNumber }
3123
- });
3132
+ }, initial);
3124
3133
  if (check(), page.questions.some((question2) => question2.inboxUrl !== `https://${initial.workspace}.oddington.ai/inbox/${question2.itemId}` || !routines.includes(question2.routine)) || beforeNumber !== void 0 && page.nextBeforeNumber !== null && page.nextBeforeNumber >= beforeNumber) throw unavailable3();
3125
3134
  if (questions.push(...page.questions), page.nextBeforeNumber === null) return questions;
3126
3135
  beforeNumber = page.nextBeforeNumber;