@palettelab/sdk 0.1.15 → 0.1.17

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/index.mjs CHANGED
@@ -110,6 +110,32 @@ async function updateInstallConfig(pluginId, config) {
110
110
  return res.json();
111
111
  }
112
112
 
113
+ // src/connections.ts
114
+ async function getConnections(pluginId) {
115
+ const res = await apiFetch(`/api/v1/app-installs/${encodeURIComponent(pluginId)}/connections`);
116
+ return res.json();
117
+ }
118
+ async function startConnection(pluginId, connectionId) {
119
+ const res = await apiFetch(
120
+ `/api/v1/app-installs/${encodeURIComponent(pluginId)}/connections/${encodeURIComponent(connectionId)}/authorize`,
121
+ { method: "POST" }
122
+ );
123
+ return res.json();
124
+ }
125
+ async function disconnectConnection(pluginId, connectionId) {
126
+ await apiFetch(
127
+ `/api/v1/app-installs/${encodeURIComponent(pluginId)}/connections/${encodeURIComponent(connectionId)}`,
128
+ { method: "DELETE" }
129
+ );
130
+ }
131
+ async function requireConnection(pluginId, connectionId) {
132
+ const connection = (await getConnections(pluginId)).find((item) => item.id === connectionId);
133
+ if (!connection || !["connected", "expiring"].includes(connection.status)) {
134
+ throw new Error(`Palette connection is not configured: ${connectionId}`);
135
+ }
136
+ return connection;
137
+ }
138
+
113
139
  // src/test-utils.tsx
114
140
  import { createElement } from "react";
115
141
 
@@ -171,6 +197,7 @@ function createMockPlatformContext(overrides = {}) {
171
197
  apiFetch: async () => new Response(JSON.stringify({}), { status: 200 }),
172
198
  navigate: () => {
173
199
  },
200
+ routePath: "/",
174
201
  showToast: () => {
175
202
  },
176
203
  ...overrides
@@ -584,6 +611,28 @@ function createPaletteClient(ctx) {
584
611
  return updateInstallConfig(resolved, values);
585
612
  }
586
613
  },
614
+ connections: {
615
+ list: (pluginId = ctx?.pluginId ?? "") => {
616
+ const resolved = pluginId || defaultPluginId2();
617
+ if (!resolved) throw new Error("pluginId is required to read connections");
618
+ return getConnections(resolved);
619
+ },
620
+ connect: (connectionId, pluginId = ctx?.pluginId ?? "") => {
621
+ const resolved = pluginId || defaultPluginId2();
622
+ if (!resolved) throw new Error("pluginId is required to start a connection");
623
+ return startConnection(resolved, connectionId);
624
+ },
625
+ disconnect: (connectionId, pluginId = ctx?.pluginId ?? "") => {
626
+ const resolved = pluginId || defaultPluginId2();
627
+ if (!resolved) throw new Error("pluginId is required to disconnect a connection");
628
+ return disconnectConnection(resolved, connectionId);
629
+ },
630
+ require: (connectionId, pluginId = ctx?.pluginId ?? "") => {
631
+ const resolved = pluginId || defaultPluginId2();
632
+ if (!resolved) throw new Error("pluginId is required to require a connection");
633
+ return requireConnection(resolved, connectionId);
634
+ }
635
+ },
587
636
  permissions: {
588
637
  has: (permission) => ctx ? hasPermission(ctx, permission) : false,
589
638
  hasAny: (permissions) => ctx ? hasAnyPermission(ctx, permissions) : false,
@@ -660,14 +709,216 @@ function usePluginTranslations(resources, options = {}) {
660
709
  };
661
710
  }
662
711
 
712
+ // src/router.tsx
713
+ import {
714
+ Component,
715
+ createContext as createContext2,
716
+ createElement as createElement2,
717
+ useCallback as useCallback2,
718
+ useContext as useContext2,
719
+ useEffect,
720
+ useMemo,
721
+ useState
722
+ } from "react";
723
+ var RouterCtx = createContext2(null);
724
+ var NOT_FOUND = /* @__PURE__ */ Symbol.for("palette.router.not-found");
725
+ function notFound() {
726
+ throw Object.assign(new Error("Palette route not found"), { code: NOT_FOUND });
727
+ }
728
+ function normalizePath(path) {
729
+ const clean = String(path || "/").split("#")[0].split("?")[0] || "/";
730
+ return `/${clean.replace(/^\/+/, "").replace(/\/+$/, "")}`.replace(/^\/$/, "/");
731
+ }
732
+ function splitPath(path) {
733
+ return normalizePath(path).split("/").filter(Boolean).map(decodeURIComponent);
734
+ }
735
+ function routeScore(route) {
736
+ if (typeof route.score === "number") return route.score;
737
+ return route.segments.reduce((score, segment) => {
738
+ if (segment.kind === "static") return score + 10;
739
+ if (segment.kind === "dynamic") return score + 5;
740
+ return score + (segment.optional ? 1 : 2);
741
+ }, route.segments.length);
742
+ }
743
+ function matchRoute(route, path) {
744
+ const parts = splitPath(path);
745
+ const params = {};
746
+ let index = 0;
747
+ for (const segment of route.segments) {
748
+ if (segment.kind === "catchAll") {
749
+ const rest = parts.slice(index);
750
+ if (!segment.optional && rest.length === 0) return null;
751
+ params[segment.name] = rest;
752
+ index = parts.length;
753
+ break;
754
+ }
755
+ const value = parts[index];
756
+ if (value === void 0) return null;
757
+ if (segment.kind === "static") {
758
+ if (value !== segment.value) return null;
759
+ } else {
760
+ params[segment.name] = value;
761
+ }
762
+ index += 1;
763
+ }
764
+ return index === parts.length ? params : null;
765
+ }
766
+ function currentPluginPath(pluginId, routePath) {
767
+ if (routePath) return normalizePath(routePath);
768
+ if (typeof window === "undefined") return "/";
769
+ const path = window.location.pathname;
770
+ if (pluginId) {
771
+ const prefix = `/apps/${pluginId}`;
772
+ if (path === prefix) return "/";
773
+ if (path.startsWith(`${prefix}/`)) return normalizePath(path.slice(prefix.length));
774
+ }
775
+ return normalizePath(path);
776
+ }
777
+ function currentSearchParams() {
778
+ if (typeof window === "undefined") return new URLSearchParams();
779
+ return new URLSearchParams(window.location.search);
780
+ }
781
+ var PaletteRouteErrorBoundary = class extends Component {
782
+ constructor() {
783
+ super(...arguments);
784
+ this.state = { error: null };
785
+ this.reset = () => this.setState({ error: null });
786
+ }
787
+ static getDerivedStateFromError(error) {
788
+ return { error };
789
+ }
790
+ componentDidCatch(_error, _info) {
791
+ }
792
+ render() {
793
+ const error = this.state.error;
794
+ if (!error) return this.props.children;
795
+ if (error.code === NOT_FOUND && this.props.notFound) {
796
+ return createElement2(this.props.notFound);
797
+ }
798
+ if (this.props.fallback) {
799
+ return createElement2(this.props.fallback, { error, reset: this.reset });
800
+ }
801
+ throw error;
802
+ }
803
+ };
804
+ function renderRoute(route) {
805
+ const page = createElement2(route.page);
806
+ const wrapped = (route.layouts || []).reduceRight(
807
+ (children, Layout) => createElement2(Layout, null, children),
808
+ page
809
+ );
810
+ return createElement2(
811
+ PaletteRouteErrorBoundary,
812
+ { fallback: route.error, notFound: route.notFound },
813
+ wrapped
814
+ );
815
+ }
816
+ function PaletteAppRouter({
817
+ routes,
818
+ notFound: NotFound
819
+ }) {
820
+ const platform = usePlatform();
821
+ const [location, setLocation] = useState(() => ({
822
+ pathname: currentPluginPath(platform.pluginId, platform.routePath),
823
+ searchParams: currentSearchParams()
824
+ }));
825
+ useEffect(() => {
826
+ setLocation({
827
+ pathname: currentPluginPath(platform.pluginId, platform.routePath),
828
+ searchParams: currentSearchParams()
829
+ });
830
+ }, [platform.pluginId, platform.routePath]);
831
+ useEffect(() => {
832
+ const sync = () => setLocation({
833
+ pathname: currentPluginPath(platform.pluginId, platform.routePath),
834
+ searchParams: currentSearchParams()
835
+ });
836
+ window.addEventListener("popstate", sync);
837
+ return () => window.removeEventListener("popstate", sync);
838
+ }, [platform.pluginId, platform.routePath]);
839
+ const sortedRoutes = useMemo(
840
+ () => [...routes].sort((a, b) => routeScore(b) - routeScore(a)),
841
+ [routes]
842
+ );
843
+ const matched = useMemo(() => {
844
+ for (const route of sortedRoutes) {
845
+ const params = matchRoute(route, location.pathname);
846
+ if (params) return { route, params };
847
+ }
848
+ return null;
849
+ }, [location.pathname, sortedRoutes]);
850
+ const navigate = useCallback2((target, replace = false) => {
851
+ const [pathPart, queryPart = ""] = String(target || "/").split("?");
852
+ const pathname = normalizePath(pathPart);
853
+ const next = `${pathname}${queryPart ? `?${queryPart}` : ""}`;
854
+ setLocation({ pathname, searchParams: new URLSearchParams(queryPart) });
855
+ const currentPath = typeof window === "undefined" ? "" : window.location.pathname;
856
+ const inPalettePath = platform.pluginId && (currentPath.startsWith(`/apps/${platform.pluginId}`) || platform.routePath !== void 0);
857
+ const osPath = inPalettePath && platform.pluginId ? `/apps/${platform.pluginId}${pathname === "/" ? "" : pathname}${queryPart ? `?${queryPart}` : ""}` : next;
858
+ if (replace && typeof window !== "undefined") window.history.replaceState(null, "", osPath);
859
+ else platform.navigate(osPath);
860
+ }, [platform]);
861
+ const state = useMemo(() => ({
862
+ pathname: location.pathname,
863
+ searchParams: location.searchParams,
864
+ params: matched?.params || {},
865
+ push: (path) => navigate(path, false),
866
+ replace: (path) => navigate(path, true)
867
+ }), [location.pathname, location.searchParams, matched?.params, navigate]);
868
+ const content = matched ? renderRoute(matched.route) : NotFound ? createElement2(NotFound) : createElement2("div", null, "Page not found");
869
+ return createElement2(RouterCtx.Provider, { value: state }, content);
870
+ }
871
+ function useRouterState() {
872
+ const value = useContext2(RouterCtx);
873
+ if (!value) throw new Error("Palette app router hooks must be used inside PaletteAppRouter");
874
+ return value;
875
+ }
876
+ function useRouter() {
877
+ const { push, replace } = useRouterState();
878
+ return { push, replace, back: () => window.history.back(), forward: () => window.history.forward() };
879
+ }
880
+ function usePathname() {
881
+ return useRouterState().pathname;
882
+ }
883
+ function useSearchParams() {
884
+ return useRouterState().searchParams;
885
+ }
886
+ function useParams() {
887
+ return useRouterState().params;
888
+ }
889
+ function Link({
890
+ href,
891
+ replace,
892
+ onClick,
893
+ children,
894
+ ...props
895
+ }) {
896
+ const router = useRouter();
897
+ return createElement2(
898
+ "a",
899
+ {
900
+ ...props,
901
+ href,
902
+ onClick: (event) => {
903
+ onClick?.(event);
904
+ if (event.defaultPrevented || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
905
+ event.preventDefault();
906
+ if (replace) router.replace(href);
907
+ else router.push(href);
908
+ }
909
+ },
910
+ children
911
+ );
912
+ }
913
+
663
914
  // src/hooks/use-plugin-tasks.ts
664
- import { useCallback as useCallback2, useEffect, useState } from "react";
915
+ import { useCallback as useCallback3, useEffect as useEffect2, useState as useState2 } from "react";
665
916
  function usePluginTasks(agentId) {
666
917
  const { apiFetch: apiFetch2 } = usePlatform();
667
- const [tasks, setTasks] = useState([]);
668
- const [stats, setStats] = useState(null);
669
- const [loading, setLoading] = useState(true);
670
- const fetchTasks = useCallback2(async () => {
918
+ const [tasks, setTasks] = useState2([]);
919
+ const [stats, setStats] = useState2(null);
920
+ const [loading, setLoading] = useState2(true);
921
+ const fetchTasks = useCallback3(async () => {
671
922
  try {
672
923
  const params = new URLSearchParams();
673
924
  if (agentId) params.set("agent_id", String(agentId));
@@ -676,18 +927,18 @@ function usePluginTasks(agentId) {
676
927
  } catch {
677
928
  }
678
929
  }, [apiFetch2, agentId]);
679
- const fetchStats = useCallback2(async () => {
930
+ const fetchStats = useCallback3(async () => {
680
931
  try {
681
932
  const res = await apiFetch2("/api/v1/tasks/stats");
682
933
  setStats(await res.json());
683
934
  } catch {
684
935
  }
685
936
  }, [apiFetch2]);
686
- useEffect(() => {
937
+ useEffect2(() => {
687
938
  setLoading(true);
688
939
  Promise.all([fetchTasks(), fetchStats()]).finally(() => setLoading(false));
689
940
  }, [fetchTasks, fetchStats]);
690
- const createTask = useCallback2(async (payload) => {
941
+ const createTask = useCallback3(async (payload) => {
691
942
  const res = await apiFetch2("/api/v1/tasks", {
692
943
  method: "POST",
693
944
  body: JSON.stringify(payload)
@@ -696,7 +947,7 @@ function usePluginTasks(agentId) {
696
947
  await Promise.all([fetchTasks(), fetchStats()]);
697
948
  return task;
698
949
  }, [apiFetch2, fetchTasks, fetchStats]);
699
- const updateTask = useCallback2(async (taskId, payload) => {
950
+ const updateTask = useCallback3(async (taskId, payload) => {
700
951
  const res = await apiFetch2(`/api/v1/tasks/${taskId}`, {
701
952
  method: "PATCH",
702
953
  body: JSON.stringify(payload)
@@ -705,7 +956,7 @@ function usePluginTasks(agentId) {
705
956
  await Promise.all([fetchTasks(), fetchStats()]);
706
957
  return task;
707
958
  }, [apiFetch2, fetchTasks, fetchStats]);
708
- const deleteTask = useCallback2(async (taskId) => {
959
+ const deleteTask = useCallback3(async (taskId) => {
709
960
  await apiFetch2(`/api/v1/tasks/${taskId}`, { method: "DELETE" });
710
961
  await Promise.all([fetchTasks(), fetchStats()]);
711
962
  }, [apiFetch2, fetchTasks, fetchStats]);
@@ -713,23 +964,23 @@ function usePluginTasks(agentId) {
713
964
  }
714
965
 
715
966
  // src/hooks/use-plugin-data-rooms.ts
716
- import { useCallback as useCallback3, useEffect as useEffect2, useState as useState2 } from "react";
967
+ import { useCallback as useCallback4, useEffect as useEffect3, useState as useState3 } from "react";
717
968
  function usePluginDataRooms() {
718
969
  const { apiFetch: apiFetch2 } = usePlatform();
719
- const [rooms, setRooms] = useState2([]);
720
- const [loading, setLoading] = useState2(true);
721
- const fetchRooms = useCallback3(async () => {
970
+ const [rooms, setRooms] = useState3([]);
971
+ const [loading, setLoading] = useState3(true);
972
+ const fetchRooms = useCallback4(async () => {
722
973
  try {
723
974
  const res = await apiFetch2("/api/v1/data-rooms");
724
975
  setRooms(await res.json());
725
976
  } catch {
726
977
  }
727
978
  }, [apiFetch2]);
728
- useEffect2(() => {
979
+ useEffect3(() => {
729
980
  setLoading(true);
730
981
  fetchRooms().finally(() => setLoading(false));
731
982
  }, [fetchRooms]);
732
- const fetchFolder = useCallback3(async (roomId, folderId) => {
983
+ const fetchFolder = useCallback4(async (roomId, folderId) => {
733
984
  const path = folderId ? `/api/v1/data-rooms/${roomId}/folders/${folderId}` : `/api/v1/data-rooms/${roomId}`;
734
985
  const res = await apiFetch2(path);
735
986
  return res.json();
@@ -738,19 +989,19 @@ function usePluginDataRooms() {
738
989
  }
739
990
 
740
991
  // src/hooks/use-plugin-chat.ts
741
- import { useCallback as useCallback4, useRef, useState as useState3 } from "react";
992
+ import { useCallback as useCallback5, useRef, useState as useState4 } from "react";
742
993
  function usePluginChat(agentId) {
743
994
  const { apiFetch: apiFetch2 } = usePlatform();
744
- const [threads, setThreads] = useState3([]);
745
- const [messages, setMessages] = useState3([]);
746
- const [streaming, setStreaming] = useState3(false);
747
- const [activeThreadId, setActiveThreadId] = useState3(null);
995
+ const [threads, setThreads] = useState4([]);
996
+ const [messages, setMessages] = useState4([]);
997
+ const [streaming, setStreaming] = useState4(false);
998
+ const [activeThreadId, setActiveThreadId] = useState4(null);
748
999
  const abortRef = useRef(null);
749
- const fetchThreads = useCallback4(async () => {
1000
+ const fetchThreads = useCallback5(async () => {
750
1001
  const res = await apiFetch2(`/api/v1/chat/${agentId}/threads`);
751
1002
  setThreads(await res.json());
752
1003
  }, [apiFetch2, agentId]);
753
- const createThread = useCallback4(async () => {
1004
+ const createThread = useCallback5(async () => {
754
1005
  const res = await apiFetch2(`/api/v1/chat/${agentId}/threads`, { method: "POST" });
755
1006
  const thread = await res.json();
756
1007
  setActiveThreadId(thread.id);
@@ -758,13 +1009,13 @@ function usePluginChat(agentId) {
758
1009
  await fetchThreads();
759
1010
  return thread;
760
1011
  }, [apiFetch2, agentId, fetchThreads]);
761
- const fetchMessages = useCallback4(async (threadId) => {
1012
+ const fetchMessages = useCallback5(async (threadId) => {
762
1013
  const res = await apiFetch2(`/api/v1/chat/threads/${threadId}/messages`);
763
1014
  const msgs = await res.json();
764
1015
  setMessages(msgs);
765
1016
  setActiveThreadId(threadId);
766
1017
  }, [apiFetch2]);
767
- const sendMessage = useCallback4(async (threadId, content) => {
1018
+ const sendMessage = useCallback5(async (threadId, content) => {
768
1019
  setStreaming(true);
769
1020
  abortRef.current = new AbortController();
770
1021
  const userMsg = {
@@ -832,7 +1083,7 @@ function usePluginChat(agentId) {
832
1083
  abortRef.current = null;
833
1084
  }
834
1085
  }, []);
835
- const stopStreaming = useCallback4(() => {
1086
+ const stopStreaming = useCallback5(() => {
836
1087
  abortRef.current?.abort();
837
1088
  }, []);
838
1089
  return {
@@ -849,8 +1100,10 @@ function usePluginChat(agentId) {
849
1100
  }
850
1101
  export {
851
1102
  DataRoomClient,
1103
+ Link,
852
1104
  OrganizationClient,
853
1105
  PaletteApiError,
1106
+ PaletteAppRouter,
854
1107
  PlatformCtx,
855
1108
  PluginProvider,
856
1109
  StorageClient,
@@ -861,8 +1114,10 @@ export {
861
1114
  createPaletteClient,
862
1115
  createSandboxBridge,
863
1116
  dataRooms,
1117
+ disconnectConnection,
864
1118
  errorFromResponse,
865
1119
  getBaseUrl,
1120
+ getConnections,
866
1121
  getInstallConfig,
867
1122
  hasAllPermissions,
868
1123
  hasAnyPermission,
@@ -870,14 +1125,21 @@ export {
870
1125
  isPaletteApiError,
871
1126
  isSandboxRuntime,
872
1127
  normalizePaletteLanguage,
1128
+ notFound,
1129
+ requireConnection,
873
1130
  setBaseUrl,
1131
+ startConnection,
874
1132
  translate,
875
1133
  updateInstallConfig,
876
1134
  uploadToSignedUrl,
1135
+ useParams,
1136
+ usePathname,
877
1137
  usePlatform,
878
1138
  usePluginChat,
879
1139
  usePluginDataRooms,
880
1140
  usePluginTasks,
881
1141
  usePluginTranslations,
1142
+ useRouter,
1143
+ useSearchParams,
882
1144
  withPluginProvider
883
1145
  };
@@ -106,7 +106,7 @@ interface PluginManifest {
106
106
  frontend?: {
107
107
  entry: string;
108
108
  sandbox?: boolean;
109
- framework?: "react" | "next";
109
+ framework?: "react" | "next" | "palette-app";
110
110
  config?: string;
111
111
  };
112
112
  backend?: {
@@ -116,6 +116,9 @@ interface PluginManifest {
116
116
  agents?: PluginAgentDefinition[];
117
117
  tools?: PluginToolDefinition[];
118
118
  permissions?: string[];
119
+ connections?: PluginConnectionDefinition[];
120
+ provides?: PluginProvidesDefinition;
121
+ requires?: PluginRequiresDefinition;
119
122
  public_routes?: string[];
120
123
  rate_limit?: {
121
124
  per_minute?: number;
@@ -124,6 +127,46 @@ interface PluginManifest {
124
127
  reviews?: number;
125
128
  featured?: boolean;
126
129
  }
130
+ interface PluginConnectionDefinition {
131
+ id: string;
132
+ provider: "google" | "instagram" | "slack" | "linear" | "hubspot" | "stripe" | "zendesk" | "custom";
133
+ label: string;
134
+ auth?: "oauth2" | "api_key";
135
+ scopes?: string[];
136
+ required?: boolean;
137
+ }
138
+ interface PluginServiceRouteDefinition {
139
+ method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
140
+ path: string;
141
+ operation_id?: string;
142
+ description?: string;
143
+ }
144
+ interface PluginServiceDefinition {
145
+ id: string;
146
+ version?: string;
147
+ label?: string;
148
+ description?: string;
149
+ permissions?: string[];
150
+ routes?: PluginServiceRouteDefinition[];
151
+ }
152
+ interface PluginProvidesDefinition {
153
+ services?: PluginServiceDefinition[];
154
+ events?: string[];
155
+ }
156
+ interface PluginAppDependencyDefinition {
157
+ id: string;
158
+ version?: string;
159
+ required?: boolean;
160
+ reason?: string;
161
+ }
162
+ interface PluginServiceDependencyDefinition extends PluginAppDependencyDefinition {
163
+ provider?: string;
164
+ }
165
+ interface PluginRequiresDefinition {
166
+ apps?: PluginAppDependencyDefinition[];
167
+ services?: PluginServiceDependencyDefinition[];
168
+ events?: string[];
169
+ }
127
170
  interface PluginAgentDefinition {
128
171
  name: string;
129
172
  field: string;
@@ -165,6 +208,8 @@ interface PlatformContext {
165
208
  apiFetch: (path: string, init?: RequestInit) => Promise<Response>;
166
209
  /** Navigate to a platform route */
167
210
  navigate: (path: string) => void;
211
+ /** Current route path within the plugin app, when mounted by Palette OS */
212
+ routePath?: string;
168
213
  /** Show a toast notification */
169
214
  showToast: (message: string, type?: "success" | "error" | "info") => void;
170
215
  /** Permissions declared for the current plugin install/runtime */
@@ -175,4 +220,4 @@ interface PluginComponentProps {
175
220
  platform: PlatformContext;
176
221
  }
177
222
 
178
- export type { Agent as A, OrgSummary as O, PlatformContext as P, User as U, AgentConfig as a, AppCategory as b, AuthContextValue as c, PaletteLanguage as d, PluginAgentDefinition as e, PluginComponentProps as f, PluginManifest as g, PluginToolDefinition as h };
223
+ export type { Agent as A, OrgSummary as O, PlatformContext as P, User as U, AgentConfig as a, AppCategory as b, AuthContextValue as c, PaletteLanguage as d, PluginAgentDefinition as e, PluginComponentProps as f, PluginConnectionDefinition as g, PluginManifest as h, PluginToolDefinition as i };
@@ -106,7 +106,7 @@ interface PluginManifest {
106
106
  frontend?: {
107
107
  entry: string;
108
108
  sandbox?: boolean;
109
- framework?: "react" | "next";
109
+ framework?: "react" | "next" | "palette-app";
110
110
  config?: string;
111
111
  };
112
112
  backend?: {
@@ -116,6 +116,9 @@ interface PluginManifest {
116
116
  agents?: PluginAgentDefinition[];
117
117
  tools?: PluginToolDefinition[];
118
118
  permissions?: string[];
119
+ connections?: PluginConnectionDefinition[];
120
+ provides?: PluginProvidesDefinition;
121
+ requires?: PluginRequiresDefinition;
119
122
  public_routes?: string[];
120
123
  rate_limit?: {
121
124
  per_minute?: number;
@@ -124,6 +127,46 @@ interface PluginManifest {
124
127
  reviews?: number;
125
128
  featured?: boolean;
126
129
  }
130
+ interface PluginConnectionDefinition {
131
+ id: string;
132
+ provider: "google" | "instagram" | "slack" | "linear" | "hubspot" | "stripe" | "zendesk" | "custom";
133
+ label: string;
134
+ auth?: "oauth2" | "api_key";
135
+ scopes?: string[];
136
+ required?: boolean;
137
+ }
138
+ interface PluginServiceRouteDefinition {
139
+ method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
140
+ path: string;
141
+ operation_id?: string;
142
+ description?: string;
143
+ }
144
+ interface PluginServiceDefinition {
145
+ id: string;
146
+ version?: string;
147
+ label?: string;
148
+ description?: string;
149
+ permissions?: string[];
150
+ routes?: PluginServiceRouteDefinition[];
151
+ }
152
+ interface PluginProvidesDefinition {
153
+ services?: PluginServiceDefinition[];
154
+ events?: string[];
155
+ }
156
+ interface PluginAppDependencyDefinition {
157
+ id: string;
158
+ version?: string;
159
+ required?: boolean;
160
+ reason?: string;
161
+ }
162
+ interface PluginServiceDependencyDefinition extends PluginAppDependencyDefinition {
163
+ provider?: string;
164
+ }
165
+ interface PluginRequiresDefinition {
166
+ apps?: PluginAppDependencyDefinition[];
167
+ services?: PluginServiceDependencyDefinition[];
168
+ events?: string[];
169
+ }
127
170
  interface PluginAgentDefinition {
128
171
  name: string;
129
172
  field: string;
@@ -165,6 +208,8 @@ interface PlatformContext {
165
208
  apiFetch: (path: string, init?: RequestInit) => Promise<Response>;
166
209
  /** Navigate to a platform route */
167
210
  navigate: (path: string) => void;
211
+ /** Current route path within the plugin app, when mounted by Palette OS */
212
+ routePath?: string;
168
213
  /** Show a toast notification */
169
214
  showToast: (message: string, type?: "success" | "error" | "info") => void;
170
215
  /** Permissions declared for the current plugin install/runtime */
@@ -175,4 +220,4 @@ interface PluginComponentProps {
175
220
  platform: PlatformContext;
176
221
  }
177
222
 
178
- export type { Agent as A, OrgSummary as O, PlatformContext as P, User as U, AgentConfig as a, AppCategory as b, AuthContextValue as c, PaletteLanguage as d, PluginAgentDefinition as e, PluginComponentProps as f, PluginManifest as g, PluginToolDefinition as h };
223
+ export type { Agent as A, OrgSummary as O, PlatformContext as P, User as U, AgentConfig as a, AppCategory as b, AuthContextValue as c, PaletteLanguage as d, PluginAgentDefinition as e, PluginComponentProps as f, PluginConnectionDefinition as g, PluginManifest as h, PluginToolDefinition as i };