@foir/sdk 0.29.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.js CHANGED
@@ -1242,6 +1242,12 @@ var GRANT_TABLE = [
1242
1242
  ["operations:write", "none", [], []],
1243
1243
  ["operations:complete", "none", [], []],
1244
1244
  ["status:write", "none", [], []],
1245
+ ["agents:read", "none", [], []],
1246
+ ["agents:execute", "agent", [], []],
1247
+ ["agents:write", "none", [], []],
1248
+ ["agenttools:execute", "agent_tool", [], []],
1249
+ ["brief:read", "none", [], []],
1250
+ ["brief:write", "none", [], []],
1245
1251
  ["appconfig:read", "none", [], []],
1246
1252
  ["schedules:read", "none", [], []],
1247
1253
  ["schedules:write", "none", [], []],
@@ -1291,7 +1297,7 @@ var GRANT_TABLE = [
1291
1297
  ["projects:read", "none", [], []],
1292
1298
  ["projects:write", "none", [], []],
1293
1299
  ["project:stand", "none", [], []],
1294
- ["project:manage", "none", [], ["project:stand", "records:read.all", "records:create", "records:write.all", "records:delete.all", "records:publish.all", "records:attribute", "search:read", "embeddings:read", "embeddings:write", "schemas:read", "drafts:read", "files:read.all", "files:write.all", "files:delete.all", "users:read.all", "identities:read", "users:create", "identities:delete", "users:write.all", "users:setup", "users:delete.all", "notifications:read.all", "notifications:write.all", "notifications:consent", "pushcredentials:read", "pushcredentials:write", "notes:read.all", "notes:write.all", "notes:create", "approvals:read", "approvals:write", "rooms:read", "rooms:write", "presence:read", "operations:read", "operations:execute", "operations:write", "appconfig:read", "schedules:read", "schedules:write", "hooks:read", "hooks:write", "authclients:read", "authclients:write", "domains:read", "domains:write", "loginpages:read", "loginpages:write", "secrets:read", "secrets:list", "secrets:put", "secrets:delete", "appsecrets:read", "appsecrets:list", "appsecrets:put", "appsecrets:delete", "usersecrets:read", "usersecrets:list", "usersecrets:put", "usersecrets:delete", "opsecrets:read", "opsecrets:write", "settings:read", "settings:write", "designtokens:read", "designtokens:write", "models:read", "models:write", "configs:read", "configs:write", "signinproviders:read", "signinproviders:write", "apps:read", "apps:write", "events:read", "targeting:debug", "visitors:forward", "apikeys:read", "apikeys:write", "organizations:read", "organizations:create", "organizations:delete", "organizations:write", "orgsettings:write", "organizations:reach", "members:read", "members:write", "roles:read", "roles:write", "projects:read", "projects:write", "audit:read"]],
1300
+ ["project:manage", "none", [], ["project:stand", "records:read.all", "records:create", "records:write.all", "records:delete.all", "records:publish.all", "records:attribute", "search:read", "embeddings:read", "embeddings:write", "schemas:read", "drafts:read", "files:read.all", "files:write.all", "files:delete.all", "users:read.all", "identities:read", "users:create", "identities:delete", "users:write.all", "users:setup", "users:delete.all", "notifications:read.all", "notifications:write.all", "notifications:consent", "pushcredentials:read", "pushcredentials:write", "notes:read.all", "notes:write.all", "notes:create", "approvals:read", "approvals:write", "rooms:read", "rooms:write", "presence:read", "operations:read", "operations:execute", "operations:write", "agents:read", "agents:execute", "agents:write", "agenttools:execute", "brief:read", "brief:write", "appconfig:read", "schedules:read", "schedules:write", "hooks:read", "hooks:write", "authclients:read", "authclients:write", "domains:read", "domains:write", "loginpages:read", "loginpages:write", "secrets:read", "secrets:list", "secrets:put", "secrets:delete", "appsecrets:read", "appsecrets:list", "appsecrets:put", "appsecrets:delete", "usersecrets:read", "usersecrets:list", "usersecrets:put", "usersecrets:delete", "opsecrets:read", "opsecrets:write", "settings:read", "settings:write", "designtokens:read", "designtokens:write", "models:read", "models:write", "configs:read", "configs:write", "signinproviders:read", "signinproviders:write", "apps:read", "apps:write", "events:read", "targeting:debug", "visitors:forward", "apikeys:read", "apikeys:write", "organizations:read", "organizations:create", "organizations:delete", "organizations:write", "orgsettings:write", "organizations:reach", "members:read", "members:write", "roles:read", "roles:write", "projects:read", "projects:write", "audit:read"]],
1295
1301
  ["projects:create", "none", [], []],
1296
1302
  ["projects:delete", "none", [], []],
1297
1303
  ["tenants:read", "none", [], []],
@@ -2263,7 +2269,7 @@ async function submitFeedbackNote(client, submission) {
2263
2269
  await uploadFeedbackAttachment(client, submission.screenshot, submission.fetch)
2264
2270
  );
2265
2271
  }
2266
- const args = noteArgs(submission.subject, submission.body);
2272
+ const args = noteArgs(submission.subject, submission.body, submission.anchorField);
2267
2273
  if (attachmentFileIds.length > 0) args.attachmentFileIds = attachmentFileIds;
2268
2274
  const { notes } = client.ns;
2269
2275
  return readCreatedNote(
@@ -2277,7 +2283,11 @@ async function submitVisitorNote(client, submission) {
2277
2283
  "[foir] this client has no `notes.writeNote`, which is the only create that takes a note from a visitor who is not signed in. Re-run `foir types`."
2278
2284
  );
2279
2285
  }
2280
- const args = noteArgs(submission.subject, submission.body);
2286
+ const args = noteArgs(
2287
+ submission.subject,
2288
+ submission.body,
2289
+ submission.anchorField
2290
+ );
2281
2291
  const contactEmail = submission.contactEmail?.trim();
2282
2292
  if (contactEmail) args.contactEmail = contactEmail;
2283
2293
  args.pick = ["id", "createdAt"];
@@ -2292,13 +2302,15 @@ async function submitVisitorNote(client, submission) {
2292
2302
  createdAt: typeof result.createdAt === "string" ? result.createdAt : ""
2293
2303
  };
2294
2304
  }
2295
- function noteArgs(subject, body) {
2296
- return {
2305
+ function noteArgs(subject, body, anchorField) {
2306
+ const args = {
2297
2307
  subjectType: "record",
2298
2308
  subjectId: subject.recordId,
2299
2309
  subjectModelKey: subject.modelKey,
2300
2310
  body
2301
2311
  };
2312
+ if (anchorField) args.anchorField = anchorField;
2313
+ return args;
2302
2314
  }
2303
2315
  async function uploadFeedbackAttachment(client, screenshot, fetchImpl = globalThis.fetch) {
2304
2316
  const files = client.ns.files;
@@ -2422,7 +2434,11 @@ var COPY = {
2422
2434
  captureFailed: "The screenshot could not be taken. You can still send your feedback.",
2423
2435
  screenshotAlt: "The screenshot that will be sent with your feedback",
2424
2436
  removeScreenshot: "Remove screenshot",
2425
- about: "About"
2437
+ about: "About",
2438
+ point: "Point at what you mean",
2439
+ pointAgain: "Point at something else",
2440
+ pointHint: "Click the part of the page you mean, or use Tab and Enter. Escape cancels.",
2441
+ pointCancel: "Cancel"
2426
2442
  };
2427
2443
  var border = "1px solid var(--foir-feedback-border, currentColor)";
2428
2444
  var radius = "var(--foir-feedback-radius, 8px)";
@@ -2529,6 +2545,8 @@ function FoirFeedback({
2529
2545
  onSignIn,
2530
2546
  signIn = true,
2531
2547
  layout = "inline",
2548
+ launcher,
2549
+ pointing = false,
2532
2550
  appearance,
2533
2551
  onSubmitted,
2534
2552
  onVisitorSubmitted,
@@ -2544,10 +2562,32 @@ function FoirFeedback({
2544
2562
  const [busy, setBusy] = useState4(false);
2545
2563
  const [failure, setFailure] = useState4(null);
2546
2564
  const [sent, setSent] = useState4(false);
2565
+ const [pointed, setPointed] = useState4(null);
2566
+ const [pointingNow, setPointingNow] = useState4(false);
2567
+ const [canPoint, setCanPoint] = useState4(false);
2547
2568
  const onErrorRef = useRef2(onError);
2548
2569
  onErrorRef.current = onError;
2549
- const resolved = useResolvedSubject(subjects, subject);
2570
+ const fetched = useResolvedSubject(subjects, subject);
2571
+ const offersPointing = pointing && subjects !== void 0 && subject !== null;
2572
+ const resolved = offersPointing && pointed ? pointed.subject : fetched;
2550
2573
  const styled = !className;
2574
+ useEffect3(() => {
2575
+ if (!open || !offersPointing || !subjects) return;
2576
+ setCanPoint(markedFields(subjects).length > 0);
2577
+ }, [open, offersPointing, subjects]);
2578
+ const highlight = usePointing(pointingNow ? subjects : void 0, {
2579
+ onPick: (mark) => {
2580
+ setPointed(mark);
2581
+ setPointingNow(false);
2582
+ setSent(false);
2583
+ setFailure(null);
2584
+ setOpen(true);
2585
+ },
2586
+ onCancel: () => {
2587
+ setPointingNow(false);
2588
+ setOpen(true);
2589
+ }
2590
+ });
2551
2591
  useNoSubjectNotice(open, resolved, subjects !== void 0, subject !== void 0);
2552
2592
  useEffect3(() => {
2553
2593
  if (!open) return;
@@ -2613,6 +2653,7 @@ function FoirFeedback({
2613
2653
  const note = await submitFeedbackNote(client, {
2614
2654
  subject: resolved,
2615
2655
  body: prose,
2656
+ anchorField: pointed?.field,
2616
2657
  screenshot
2617
2658
  });
2618
2659
  setBody("");
@@ -2623,6 +2664,7 @@ function FoirFeedback({
2623
2664
  const written = await submitVisitorNote(client, {
2624
2665
  subject: resolved,
2625
2666
  body: prose,
2667
+ anchorField: pointed?.field,
2626
2668
  contactEmail
2627
2669
  });
2628
2670
  setBody("");
@@ -2654,9 +2696,11 @@ function FoirFeedback({
2654
2696
  }
2655
2697
  setSent(false);
2656
2698
  setFailure(null);
2699
+ setPointed(null);
2657
2700
  setOpen(true);
2658
2701
  };
2659
- const subjectName = resolved ? label ?? resolved.label : void 0;
2702
+ const recordName = resolved ? pointed ? resolved.label : label ?? resolved.label : void 0;
2703
+ const subjectName = [recordName, pointed?.label].filter(Boolean).join(" \xB7 ") || void 0;
2660
2704
  const dialog = layout === "dialog";
2661
2705
  const heading = /* @__PURE__ */ jsx5("h2", { "data-foir-feedback-title": true, style: styled ? headingStyle : void 0, children: title });
2662
2706
  const panel = open && /* @__PURE__ */ jsxs4(
@@ -2684,6 +2728,19 @@ function FoirFeedback({
2684
2728
  ": ",
2685
2729
  subjectName
2686
2730
  ] }),
2731
+ offersPointing && canPoint && probe.status === "ready" && /* @__PURE__ */ jsx5(
2732
+ "button",
2733
+ {
2734
+ type: "button",
2735
+ "data-foir-feedback-point": true,
2736
+ onClick: () => {
2737
+ setOpen(false);
2738
+ setPointingNow(true);
2739
+ },
2740
+ style: styled ? buttonStyle : void 0,
2741
+ children: pointed ? COPY.pointAgain : COPY.point
2742
+ }
2743
+ ),
2687
2744
  probe.status === "loading" && /* @__PURE__ */ jsx5(
2688
2745
  "p",
2689
2746
  {
@@ -2798,12 +2855,19 @@ function FoirFeedback({
2798
2855
  ]
2799
2856
  }
2800
2857
  );
2858
+ const launcherPlace = launcher ? {
2859
+ position: "fixed",
2860
+ bottom: 16,
2861
+ [launcher === "bottom-left" ? "left" : "right"]: 16,
2862
+ zIndex: LAUNCHER_LAYER
2863
+ } : void 0;
2801
2864
  return /* @__PURE__ */ jsxs4(
2802
2865
  "div",
2803
2866
  {
2804
2867
  className,
2805
2868
  "data-foir-feedback": true,
2806
2869
  "data-foir-feedback-layout": layout,
2870
+ "data-foir-feedback-launcher": launcher,
2807
2871
  style: styled ? wrapperStyle(appearance) : void 0,
2808
2872
  children: [
2809
2873
  /* @__PURE__ */ jsx5(
@@ -2814,10 +2878,25 @@ function FoirFeedback({
2814
2878
  "aria-expanded": open,
2815
2879
  "aria-haspopup": dialog ? "dialog" : void 0,
2816
2880
  onClick: toggle,
2817
- style: styled ? buttonStyle : void 0,
2881
+ style: {
2882
+ ...styled ? buttonStyle : void 0,
2883
+ ...styled && launcher ? launcherStyle : void 0,
2884
+ ...launcherPlace
2885
+ },
2818
2886
  children: triggerLabel
2819
2887
  }
2820
2888
  ),
2889
+ pointingNow && /* @__PURE__ */ jsx5(
2890
+ PointingChrome,
2891
+ {
2892
+ highlight,
2893
+ styled,
2894
+ onCancel: () => {
2895
+ setPointingNow(false);
2896
+ setOpen(true);
2897
+ }
2898
+ }
2899
+ ),
2821
2900
  dialog ? /* @__PURE__ */ jsx5(
2822
2901
  "dialog",
2823
2902
  {
@@ -2831,11 +2910,184 @@ function FoirFeedback({
2831
2910
  style: styled ? dialogStyle : void 0,
2832
2911
  children: panel
2833
2912
  }
2913
+ ) : launcher && panel ? /* @__PURE__ */ jsx5(
2914
+ "div",
2915
+ {
2916
+ "data-foir-feedback-launcher-panel": true,
2917
+ style: {
2918
+ position: "fixed",
2919
+ bottom: 64,
2920
+ [launcher === "bottom-left" ? "left" : "right"]: 16,
2921
+ zIndex: LAUNCHER_LAYER,
2922
+ ...styled ? { background: "var(--foir-feedback-background, Canvas)" } : void 0
2923
+ },
2924
+ children: panel
2925
+ }
2834
2926
  ) : panel
2835
2927
  ]
2836
2928
  }
2837
2929
  );
2838
2930
  }
2931
+ var LAUNCHER_LAYER = 2147483e3;
2932
+ var POINTING_LAYER = 2147483600;
2933
+ var launcherStyle = {
2934
+ background: "var(--foir-feedback-primary, Canvas)",
2935
+ color: "var(--foir-feedback-primary-text, CanvasText)",
2936
+ boxShadow: "0 4px 16px rgb(0 0 0 / 0.18)",
2937
+ padding: "10px 14px",
2938
+ fontWeight: 600
2939
+ };
2940
+ function markedFields(subjects) {
2941
+ if (typeof document === "undefined") return [];
2942
+ return Array.from(document.querySelectorAll("[data-foir-field]")).filter(
2943
+ (el) => subjects.pointedAt(el.getAttribute("data-foir-field") ?? "") !== void 0 && el.getClientRects().length > 0
2944
+ );
2945
+ }
2946
+ function markedFieldAt(target, subjects) {
2947
+ if (!(target instanceof Element)) return null;
2948
+ const el = target.closest("[data-foir-field]");
2949
+ if (!el) return null;
2950
+ const mark = subjects.pointedAt(el.getAttribute("data-foir-field") ?? "");
2951
+ return mark ? { el, mark } : null;
2952
+ }
2953
+ function usePointing(subjects, handlers) {
2954
+ const [highlight, setHighlight] = useState4(null);
2955
+ const handlersRef = useRef2(handlers);
2956
+ handlersRef.current = handlers;
2957
+ useEffect3(() => {
2958
+ if (!subjects) return;
2959
+ let current = null;
2960
+ let index2 = -1;
2961
+ const show = (el) => {
2962
+ current = el;
2963
+ if (!el) {
2964
+ setHighlight(null);
2965
+ return;
2966
+ }
2967
+ const box = el.getBoundingClientRect();
2968
+ setHighlight({ top: box.top, left: box.left, width: box.width, height: box.height });
2969
+ };
2970
+ const ours = (target) => target instanceof Element && target.closest("[data-foir-feedback-pointing]") !== null;
2971
+ const onMove = (event) => {
2972
+ if (ours(event.target)) return;
2973
+ show(markedFieldAt(event.target, subjects)?.el ?? null);
2974
+ };
2975
+ const onClick = (event) => {
2976
+ if (ours(event.target)) return;
2977
+ event.preventDefault();
2978
+ event.stopPropagation();
2979
+ const hit = markedFieldAt(event.target, subjects);
2980
+ if (hit) handlersRef.current.onPick(hit.mark);
2981
+ };
2982
+ const onKey = (event) => {
2983
+ if (event.key === "Escape") {
2984
+ event.preventDefault();
2985
+ handlersRef.current.onCancel();
2986
+ return;
2987
+ }
2988
+ if (event.key === "Tab") {
2989
+ const fields = markedFields(subjects);
2990
+ if (fields.length === 0) return;
2991
+ event.preventDefault();
2992
+ index2 = (index2 + (event.shiftKey ? -1 : 1) + fields.length) % fields.length;
2993
+ const el = fields[index2];
2994
+ el.scrollIntoView?.({ block: "nearest" });
2995
+ show(el);
2996
+ return;
2997
+ }
2998
+ if (event.key === "Enter" && current) {
2999
+ const mark = subjects.pointedAt(current.getAttribute("data-foir-field") ?? "");
3000
+ if (!mark) return;
3001
+ event.preventDefault();
3002
+ handlersRef.current.onPick(mark);
3003
+ }
3004
+ };
3005
+ const onScroll = () => show(current);
3006
+ const root = document.documentElement;
3007
+ const cursor = root.style.cursor;
3008
+ root.style.cursor = "crosshair";
3009
+ document.addEventListener("pointermove", onMove, true);
3010
+ document.addEventListener("click", onClick, true);
3011
+ document.addEventListener("keydown", onKey, true);
3012
+ window.addEventListener("scroll", onScroll, true);
3013
+ return () => {
3014
+ root.style.cursor = cursor;
3015
+ document.removeEventListener("pointermove", onMove, true);
3016
+ document.removeEventListener("click", onClick, true);
3017
+ document.removeEventListener("keydown", onKey, true);
3018
+ window.removeEventListener("scroll", onScroll, true);
3019
+ setHighlight(null);
3020
+ };
3021
+ }, [subjects]);
3022
+ return highlight;
3023
+ }
3024
+ function PointingChrome({
3025
+ highlight,
3026
+ styled,
3027
+ onCancel
3028
+ }) {
3029
+ return /* @__PURE__ */ jsxs4(Fragment2, { children: [
3030
+ highlight && /* @__PURE__ */ jsx5(
3031
+ "div",
3032
+ {
3033
+ "aria-hidden": "true",
3034
+ "data-foir-feedback-highlight": true,
3035
+ style: {
3036
+ position: "fixed",
3037
+ top: highlight.top - 4,
3038
+ left: highlight.left - 4,
3039
+ width: highlight.width + 8,
3040
+ height: highlight.height + 8,
3041
+ pointerEvents: "none",
3042
+ zIndex: POINTING_LAYER,
3043
+ ...styled ? {
3044
+ outline: "2px solid var(--foir-feedback-primary, Highlight)",
3045
+ borderRadius: "var(--foir-feedback-radius, 8px)"
3046
+ } : void 0
3047
+ }
3048
+ }
3049
+ ),
3050
+ /* @__PURE__ */ jsxs4(
3051
+ "div",
3052
+ {
3053
+ role: "status",
3054
+ "data-foir-feedback-pointing": true,
3055
+ style: {
3056
+ position: "fixed",
3057
+ bottom: 16,
3058
+ left: "50%",
3059
+ transform: "translateX(-50%)",
3060
+ zIndex: POINTING_LAYER,
3061
+ cursor: "auto",
3062
+ ...styled ? {
3063
+ display: "flex",
3064
+ alignItems: "center",
3065
+ gap: 12,
3066
+ padding: "10px 14px",
3067
+ borderRadius: radius,
3068
+ border,
3069
+ background: "var(--foir-feedback-background, Canvas)",
3070
+ color: "var(--foir-feedback-text, CanvasText)",
3071
+ boxShadow: "0 4px 16px rgb(0 0 0 / 0.18)"
3072
+ } : void 0
3073
+ },
3074
+ children: [
3075
+ /* @__PURE__ */ jsx5("span", { children: COPY.pointHint }),
3076
+ /* @__PURE__ */ jsx5(
3077
+ "button",
3078
+ {
3079
+ type: "button",
3080
+ "data-foir-feedback-point-cancel": true,
3081
+ onClick: onCancel,
3082
+ style: styled ? closeStyle : void 0,
3083
+ children: COPY.pointCancel
3084
+ }
3085
+ )
3086
+ ]
3087
+ }
3088
+ )
3089
+ ] });
3090
+ }
2839
3091
  async function probeRefusal(client) {
2840
3092
  try {
2841
3093
  await readNoteConfig(client);
@@ -15,7 +15,9 @@
15
15
  *
16
16
  * The platform signs webhook payloads with HMAC-SHA256 using the
17
17
  * extension's webhook secret. The signature is sent in the
18
- * `x-foir-signature` header as a hex-encoded string.
18
+ * `x-foir-signature` header as a hex-encoded string. A base64 signature of
19
+ * the same HMAC is accepted too, so a header that carries one (Shopify's
20
+ * `X-Shopify-Hmac-Sha256`, for one) verifies as it arrives.
19
21
  *
20
22
  * @example
21
23
  * ```ts
package/dist/server.js CHANGED
@@ -63,14 +63,35 @@ async function verifyWebhookSignature(payload, signature, secret) {
63
63
  key,
64
64
  encoder.encode(payload)
65
65
  );
66
- const computed = Array.from(new Uint8Array(signatureBytes)).map((b) => b.toString(16).padStart(2, "0")).join("");
67
- if (computed.length !== signature.length) return false;
66
+ const computed = new Uint8Array(signatureBytes);
67
+ const presented = signatureBytesOf(signature.trim());
68
+ if (!presented || presented.length !== computed.length) return false;
68
69
  let mismatch = 0;
69
70
  for (let i = 0; i < computed.length; i++) {
70
- mismatch |= computed.charCodeAt(i) ^ signature.charCodeAt(i);
71
+ mismatch |= computed[i] ^ presented[i];
71
72
  }
72
73
  return mismatch === 0;
73
74
  }
75
+ function signatureBytesOf(signature) {
76
+ if (/^[0-9a-fA-F]+$/.test(signature) && signature.length % 2 === 0) {
77
+ const out = new Uint8Array(signature.length / 2);
78
+ for (let i = 0; i < out.length; i++) {
79
+ out[i] = parseInt(signature.slice(i * 2, i * 2 + 2), 16);
80
+ }
81
+ return out;
82
+ }
83
+ if (!/^[A-Za-z0-9+/_-]+={0,2}$/.test(signature)) return null;
84
+ const standard = signature.replace(/-/g, "+").replace(/_/g, "/");
85
+ const padded = standard + "=".repeat((4 - standard.length % 4) % 4);
86
+ try {
87
+ const binary = atob(padded);
88
+ const out = new Uint8Array(binary.length);
89
+ for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);
90
+ return out;
91
+ } catch {
92
+ return null;
93
+ }
94
+ }
74
95
 
75
96
  // src/auth/grant-table.generated.ts
76
97
  var GRANT_TABLE = [
@@ -106,6 +127,12 @@ var GRANT_TABLE = [
106
127
  ["operations:write", "none", [], []],
107
128
  ["operations:complete", "none", [], []],
108
129
  ["status:write", "none", [], []],
130
+ ["agents:read", "none", [], []],
131
+ ["agents:execute", "agent", [], []],
132
+ ["agents:write", "none", [], []],
133
+ ["agenttools:execute", "agent_tool", [], []],
134
+ ["brief:read", "none", [], []],
135
+ ["brief:write", "none", [], []],
109
136
  ["appconfig:read", "none", [], []],
110
137
  ["schedules:read", "none", [], []],
111
138
  ["schedules:write", "none", [], []],
@@ -155,7 +182,7 @@ var GRANT_TABLE = [
155
182
  ["projects:read", "none", [], []],
156
183
  ["projects:write", "none", [], []],
157
184
  ["project:stand", "none", [], []],
158
- ["project:manage", "none", [], ["project:stand", "records:read.all", "records:create", "records:write.all", "records:delete.all", "records:publish.all", "records:attribute", "search:read", "embeddings:read", "embeddings:write", "schemas:read", "drafts:read", "files:read.all", "files:write.all", "files:delete.all", "users:read.all", "identities:read", "users:create", "identities:delete", "users:write.all", "users:setup", "users:delete.all", "notifications:read.all", "notifications:write.all", "notifications:consent", "pushcredentials:read", "pushcredentials:write", "notes:read.all", "notes:write.all", "notes:create", "approvals:read", "approvals:write", "rooms:read", "rooms:write", "presence:read", "operations:read", "operations:execute", "operations:write", "appconfig:read", "schedules:read", "schedules:write", "hooks:read", "hooks:write", "authclients:read", "authclients:write", "domains:read", "domains:write", "loginpages:read", "loginpages:write", "secrets:read", "secrets:list", "secrets:put", "secrets:delete", "appsecrets:read", "appsecrets:list", "appsecrets:put", "appsecrets:delete", "usersecrets:read", "usersecrets:list", "usersecrets:put", "usersecrets:delete", "opsecrets:read", "opsecrets:write", "settings:read", "settings:write", "designtokens:read", "designtokens:write", "models:read", "models:write", "configs:read", "configs:write", "signinproviders:read", "signinproviders:write", "apps:read", "apps:write", "events:read", "targeting:debug", "visitors:forward", "apikeys:read", "apikeys:write", "organizations:read", "organizations:create", "organizations:delete", "organizations:write", "orgsettings:write", "organizations:reach", "members:read", "members:write", "roles:read", "roles:write", "projects:read", "projects:write", "audit:read"]],
185
+ ["project:manage", "none", [], ["project:stand", "records:read.all", "records:create", "records:write.all", "records:delete.all", "records:publish.all", "records:attribute", "search:read", "embeddings:read", "embeddings:write", "schemas:read", "drafts:read", "files:read.all", "files:write.all", "files:delete.all", "users:read.all", "identities:read", "users:create", "identities:delete", "users:write.all", "users:setup", "users:delete.all", "notifications:read.all", "notifications:write.all", "notifications:consent", "pushcredentials:read", "pushcredentials:write", "notes:read.all", "notes:write.all", "notes:create", "approvals:read", "approvals:write", "rooms:read", "rooms:write", "presence:read", "operations:read", "operations:execute", "operations:write", "agents:read", "agents:execute", "agents:write", "agenttools:execute", "brief:read", "brief:write", "appconfig:read", "schedules:read", "schedules:write", "hooks:read", "hooks:write", "authclients:read", "authclients:write", "domains:read", "domains:write", "loginpages:read", "loginpages:write", "secrets:read", "secrets:list", "secrets:put", "secrets:delete", "appsecrets:read", "appsecrets:list", "appsecrets:put", "appsecrets:delete", "usersecrets:read", "usersecrets:list", "usersecrets:put", "usersecrets:delete", "opsecrets:read", "opsecrets:write", "settings:read", "settings:write", "designtokens:read", "designtokens:write", "models:read", "models:write", "configs:read", "configs:write", "signinproviders:read", "signinproviders:write", "apps:read", "apps:write", "events:read", "targeting:debug", "visitors:forward", "apikeys:read", "apikeys:write", "organizations:read", "organizations:create", "organizations:delete", "organizations:write", "orgsettings:write", "organizations:reach", "members:read", "members:write", "roles:read", "roles:write", "projects:read", "projects:write", "audit:read"]],
159
186
  ["projects:create", "none", [], []],
160
187
  ["projects:delete", "none", [], []],
161
188
  ["tenants:read", "none", [], []],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foir/sdk",
3
- "version": "0.29.0",
3
+ "version": "0.30.0",
4
4
  "description": "Official Foir SDK — editor iframe client + server-side platform helpers",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -25,6 +25,10 @@
25
25
  "types": "./dist/data/index.d.ts",
26
26
  "import": "./dist/data.js"
27
27
  },
28
+ "./ai": {
29
+ "types": "./dist/ai/index.d.ts",
30
+ "import": "./dist/ai.js"
31
+ },
28
32
  "./live": {
29
33
  "types": "./dist/live/index.d.ts",
30
34
  "import": "./dist/live.js"
@@ -122,6 +126,7 @@
122
126
  "@bufbuild/protobuf": "^2.12.0",
123
127
  "@graphql-codegen/cli": "^5.0.0",
124
128
  "@graphql-codegen/client-preset": "^4.0.0",
129
+ "ai": "^7.0.0",
125
130
  "blurhash": "^2.0.0",
126
131
  "graphql": "^16.0.0",
127
132
  "hls.js": "^1.5.0",
@@ -139,6 +144,9 @@
139
144
  "@graphql-codegen/client-preset": {
140
145
  "optional": true
141
146
  },
147
+ "ai": {
148
+ "optional": true
149
+ },
142
150
  "blurhash": {
143
151
  "optional": true
144
152
  },
@@ -165,6 +173,7 @@
165
173
  "@types/react": "^19.2.0",
166
174
  "@types/react-dom": "^19.2.0",
167
175
  "@types/ws": "^8.5.13",
176
+ "ai": "^7.0.29",
168
177
  "blurhash": "^2.0.5",
169
178
  "graphql": "^16.10.0",
170
179
  "hls.js": "^1.6.16",
@@ -177,8 +186,8 @@
177
186
  "ws": "^8.18.0",
178
187
  "y-websocket": "^3.0.0",
179
188
  "yjs": "^13.6.30",
180
- "@foir/access-vocab": "0.0.0",
181
- "@foir/proto-ts": "0.109.0"
189
+ "@foir/proto-ts": "0.109.0",
190
+ "@foir/access-vocab": "0.0.0"
182
191
  },
183
192
  "engines": {
184
193
  "node": ">=18.0.0"