@nimblebrain/synapse 0.1.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.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +248 -0
  3. package/dist/chunk-AW3YIXLE.cjs +248 -0
  4. package/dist/chunk-AW3YIXLE.cjs.map +1 -0
  5. package/dist/chunk-JZC3VC2C.js +349 -0
  6. package/dist/chunk-JZC3VC2C.js.map +1 -0
  7. package/dist/chunk-M4I222LB.js +243 -0
  8. package/dist/chunk-M4I222LB.js.map +1 -0
  9. package/dist/chunk-Q7OSHSGZ.cjs +351 -0
  10. package/dist/chunk-Q7OSHSGZ.cjs.map +1 -0
  11. package/dist/codegen/cli.cjs +85 -0
  12. package/dist/codegen/cli.cjs.map +1 -0
  13. package/dist/codegen/cli.d.cts +1 -0
  14. package/dist/codegen/cli.d.ts +1 -0
  15. package/dist/codegen/cli.js +83 -0
  16. package/dist/codegen/cli.js.map +1 -0
  17. package/dist/codegen/index.cjs +24 -0
  18. package/dist/codegen/index.cjs.map +1 -0
  19. package/dist/codegen/index.d.cts +24 -0
  20. package/dist/codegen/index.d.ts +24 -0
  21. package/dist/codegen/index.js +3 -0
  22. package/dist/codegen/index.js.map +1 -0
  23. package/dist/index.cjs +87 -0
  24. package/dist/index.cjs.map +1 -0
  25. package/dist/index.d.cts +26 -0
  26. package/dist/index.d.ts +26 -0
  27. package/dist/index.js +81 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/react/index.cjs +123 -0
  30. package/dist/react/index.cjs.map +1 -0
  31. package/dist/react/index.d.cts +30 -0
  32. package/dist/react/index.d.ts +30 -0
  33. package/dist/react/index.js +113 -0
  34. package/dist/react/index.js.map +1 -0
  35. package/dist/synapse-runtime.iife.global.js +1 -0
  36. package/dist/types-BP0SNrpo.d.cts +96 -0
  37. package/dist/types-BP0SNrpo.d.ts +96 -0
  38. package/dist/vite/index.cjs +49 -0
  39. package/dist/vite/index.cjs.map +1 -0
  40. package/dist/vite/index.d.cts +21 -0
  41. package/dist/vite/index.d.ts +21 -0
  42. package/dist/vite/index.js +47 -0
  43. package/dist/vite/index.js.map +1 -0
  44. package/package.json +89 -0
@@ -0,0 +1,113 @@
1
+ import { createSynapse } from '../chunk-JZC3VC2C.js';
2
+ import { createContext, useRef, useEffect, useState, useCallback, useSyncExternalStore, useContext } from 'react';
3
+ import { jsx } from 'react/jsx-runtime';
4
+
5
+ var SynapseContext = createContext(null);
6
+ function SynapseProvider({ children, ...options }) {
7
+ const ref = useRef(null);
8
+ if (ref.current === null) {
9
+ ref.current = createSynapse(options);
10
+ }
11
+ useEffect(() => {
12
+ return () => {
13
+ ref.current?.destroy();
14
+ ref.current = null;
15
+ };
16
+ }, []);
17
+ return /* @__PURE__ */ jsx(SynapseContext.Provider, { value: ref.current, children });
18
+ }
19
+ function useSynapseContext() {
20
+ const ctx = useContext(SynapseContext);
21
+ if (!ctx) {
22
+ throw new Error(
23
+ "useSynapse must be used within a <SynapseProvider>. Wrap your app component tree with <SynapseProvider>."
24
+ );
25
+ }
26
+ return ctx;
27
+ }
28
+
29
+ // src/react/hooks.ts
30
+ function useSynapse() {
31
+ return useSynapseContext();
32
+ }
33
+ function useCallTool(toolName) {
34
+ const synapse = useSynapseContext();
35
+ const [isPending, setIsPending] = useState(false);
36
+ const [error, setError] = useState(null);
37
+ const [data, setData] = useState(null);
38
+ const callIdRef = useRef(0);
39
+ const call = useCallback(
40
+ async (args) => {
41
+ const id = ++callIdRef.current;
42
+ setIsPending(true);
43
+ setError(null);
44
+ try {
45
+ const result = await synapse.callTool(toolName, args);
46
+ if (id === callIdRef.current) {
47
+ setData(result.data);
48
+ setIsPending(false);
49
+ }
50
+ return result;
51
+ } catch (err) {
52
+ if (id === callIdRef.current) {
53
+ const e = err instanceof Error ? err : new Error(String(err));
54
+ setError(e);
55
+ setIsPending(false);
56
+ }
57
+ throw err;
58
+ }
59
+ },
60
+ [synapse, toolName]
61
+ );
62
+ return { call, isPending, error, data };
63
+ }
64
+ function useDataSync(callback) {
65
+ const synapse = useSynapseContext();
66
+ const callbackRef = useRef(callback);
67
+ callbackRef.current = callback;
68
+ useEffect(() => {
69
+ return synapse.onDataChanged((event) => callbackRef.current(event));
70
+ }, [synapse]);
71
+ }
72
+ function useTheme() {
73
+ const synapse = useSynapseContext();
74
+ const [theme, setTheme] = useState(() => synapse.getTheme());
75
+ useEffect(() => {
76
+ setTheme(synapse.getTheme());
77
+ return synapse.onThemeChanged(setTheme);
78
+ }, [synapse]);
79
+ return theme;
80
+ }
81
+ function useAction() {
82
+ const synapse = useSynapseContext();
83
+ return useCallback(
84
+ (action, params) => synapse.action(action, params),
85
+ [synapse]
86
+ );
87
+ }
88
+ function useChat() {
89
+ const synapse = useSynapseContext();
90
+ return useCallback(
91
+ (message, context) => synapse.chat(message, context),
92
+ [synapse]
93
+ );
94
+ }
95
+ function useVisibleState() {
96
+ const synapse = useSynapseContext();
97
+ return useCallback(
98
+ (state, summary) => synapse.setVisibleState(state, summary),
99
+ [synapse]
100
+ );
101
+ }
102
+ function useStore(store) {
103
+ const state = useSyncExternalStore(
104
+ (onStoreChange) => store.subscribe(onStoreChange),
105
+ () => store.getState(),
106
+ () => store.getState()
107
+ );
108
+ return { state, dispatch: store.dispatch };
109
+ }
110
+
111
+ export { SynapseProvider, useAction, useCallTool, useChat, useDataSync, useStore, useSynapse, useTheme, useVisibleState };
112
+ //# sourceMappingURL=index.js.map
113
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/react/provider.tsx","../../src/react/hooks.ts"],"names":["useRef","useEffect"],"mappings":"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","file":"index.js","sourcesContent":["import { createContext, type ReactNode, useContext, useEffect, useRef } from \"react\";\nimport { createSynapse } from \"../core.js\";\nimport type { Synapse, SynapseOptions } from \"../types.js\";\n\nconst SynapseContext = createContext<Synapse | null>(null);\n\nexport interface SynapseProviderProps extends SynapseOptions {\n children: ReactNode;\n}\n\nexport function SynapseProvider({ children, ...options }: SynapseProviderProps) {\n const ref = useRef<Synapse | null>(null);\n\n if (ref.current === null) {\n ref.current = createSynapse(options);\n }\n\n useEffect(() => {\n // StrictMode: on double-mount, the ref may already have an instance.\n // The ref was created synchronously above, so it's always valid here.\n return () => {\n ref.current?.destroy();\n ref.current = null;\n };\n }, []);\n\n return <SynapseContext.Provider value={ref.current}>{children}</SynapseContext.Provider>;\n}\n\nexport function useSynapseContext(): Synapse {\n const ctx = useContext(SynapseContext);\n if (!ctx) {\n throw new Error(\n \"useSynapse must be used within a <SynapseProvider>. \" +\n \"Wrap your app component tree with <SynapseProvider>.\",\n );\n }\n return ctx;\n}\n","import { useCallback, useEffect, useRef, useState, useSyncExternalStore } from \"react\";\nimport type {\n ActionReducer,\n DataChangedEvent,\n Store,\n StoreDispatch,\n Synapse,\n SynapseTheme,\n ToolCallResult,\n} from \"../types.js\";\nimport { SynapseProvider, useSynapseContext } from \"./provider.js\";\n\n// Re-export provider components\nexport { SynapseProvider };\n\nexport function useSynapse(): Synapse {\n return useSynapseContext();\n}\n\nexport function useCallTool<TOutput = unknown>(\n toolName: string,\n): {\n call: (args?: Record<string, unknown>) => Promise<ToolCallResult<TOutput>>;\n isPending: boolean;\n error: Error | null;\n data: TOutput | null;\n} {\n const synapse = useSynapseContext();\n const [isPending, setIsPending] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [data, setData] = useState<TOutput | null>(null);\n const callIdRef = useRef(0);\n\n const call = useCallback(\n async (args?: Record<string, unknown>): Promise<ToolCallResult<TOutput>> => {\n const id = ++callIdRef.current;\n setIsPending(true);\n setError(null);\n\n try {\n const result = await synapse.callTool<Record<string, unknown>, TOutput>(toolName, args);\n // Stale guard: only update if this is still the latest call\n if (id === callIdRef.current) {\n setData(result.data);\n setIsPending(false);\n }\n return result;\n } catch (err) {\n if (id === callIdRef.current) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n setIsPending(false);\n }\n throw err;\n }\n },\n [synapse, toolName],\n );\n\n return { call, isPending, error, data };\n}\n\nexport function useDataSync(callback: (event: DataChangedEvent) => void): void {\n const synapse = useSynapseContext();\n const callbackRef = useRef(callback);\n callbackRef.current = callback;\n\n useEffect(() => {\n return synapse.onDataChanged((event) => callbackRef.current(event));\n }, [synapse]);\n}\n\nexport function useTheme(): SynapseTheme {\n const synapse = useSynapseContext();\n const [theme, setTheme] = useState<SynapseTheme>(() => synapse.getTheme());\n\n useEffect(() => {\n // Sync in case theme changed between render and effect\n setTheme(synapse.getTheme());\n return synapse.onThemeChanged(setTheme);\n }, [synapse]);\n\n return theme;\n}\n\nexport function useAction(): (action: string, params?: Record<string, unknown>) => void {\n const synapse = useSynapseContext();\n return useCallback(\n (action: string, params?: Record<string, unknown>) => synapse.action(action, params),\n [synapse],\n );\n}\n\nexport function useChat(): (\n message: string,\n context?: { action?: string; entity?: string },\n) => void {\n const synapse = useSynapseContext();\n return useCallback(\n (message: string, context?: { action?: string; entity?: string }) =>\n synapse.chat(message, context),\n [synapse],\n );\n}\n\nexport function useVisibleState(): (state: Record<string, unknown>, summary?: string) => void {\n const synapse = useSynapseContext();\n return useCallback(\n (state: Record<string, unknown>, summary?: string) => synapse.setVisibleState(state, summary),\n [synapse],\n );\n}\n\nexport function useStore<TState, TActions extends Record<string, ActionReducer<TState, any>>>(\n store: Store<TState, TActions>,\n): {\n state: TState;\n dispatch: StoreDispatch<TActions>;\n} {\n const state = useSyncExternalStore(\n (onStoreChange) => store.subscribe(onStoreChange),\n () => store.getState(),\n () => store.getState(),\n );\n\n return { state, dispatch: store.dispatch };\n}\n"]}
@@ -0,0 +1 @@
1
+ (function(){'use strict';var b={mode:"light",primaryColor:"#6366f1",tokens:{}};function E(r){let e=r,t=C(e?.serverInfo),n=typeof t?.name=="string"?t.name:"unknown",i=typeof e?.protocolVersion=="string"?e.protocolVersion:"unknown",s=C(e?.hostContext),l=K(s?.theme);return {isNimbleBrain:n==="nimblebrain",serverName:n,protocolVersion:i,theme:l}}function K(r){let e=C(r);if(!e)return {...b};let t=e.mode==="light"||e.mode==="dark"?e.mode:b.mode,n=typeof e.primaryColor=="string"?e.primaryColor:b.primaryColor,i=e.tokens!==null&&typeof e.tokens=="object"&&!Array.isArray(e.tokens)?e.tokens:{};return {mode:t,primaryColor:n,tokens:i}}function C(r){if(r!==null&&typeof r=="object"&&!Array.isArray(r))return r}var k=class{listener;destroyed=false;constructor(e,t){let n=t??null;this.listener=i=>{this.destroyed||this.shouldForward(i,n)&&(i.preventDefault(),e.send("ui/keydown",{key:i.key,ctrlKey:i.ctrlKey,metaKey:i.metaKey,shiftKey:i.shiftKey,altKey:i.altKey}));},document.addEventListener("keydown",this.listener);}destroy(){this.destroyed||(this.destroyed=true,document.removeEventListener("keydown",this.listener));}shouldForward(e,t){return t&&t.length===0?false:t?t.some(n=>e.key.toLowerCase()===n.key.toLowerCase()&&(n.ctrl===void 0||e.ctrlKey===n.ctrl)&&(n.meta===void 0||e.metaKey===n.meta)&&(n.shift===void 0||e.shiftKey===n.shift)&&(n.alt===void 0||e.altKey===n.alt)):!!(e.key==="Escape"||e.ctrlKey||e.metaKey)}};function M(r){return r==null?{data:null,isError:false}:A(r)?I(r):{data:r,isError:false}}function A(r){return r===null||typeof r!="object"||Array.isArray(r)?false:Array.isArray(r.content)}function H(r){if(r===null||typeof r!="object"||Array.isArray(r))return false;let e=r;return e.type==="text"&&typeof e.text=="string"}function I(r){let e=r.isError===true,t=r.content;if(t.length===0)return {data:null,isError:e};let n=t.find(H);if(!n)return {data:t,isError:e};try{return {data:JSON.parse(n.text),isError:e}}catch{return {data:n.text,isError:e}}}var R=class{counter=0;destroyed=false;pending=new Map;handlers=new Map;listener;constructor(){this.listener=e=>this.handleMessage(e),window.addEventListener("message",this.listener);}send(e,t){if(this.destroyed)return;let n={jsonrpc:"2.0",method:e,...t!==void 0&&{params:t}};window.parent.postMessage(n,"*");}request(e,t){if(this.destroyed)return Promise.reject(new Error("Transport destroyed"));let n=`syn-${++this.counter}`,i={jsonrpc:"2.0",method:e,id:n,...t!==void 0&&{params:t}};return new Promise((s,l)=>{this.pending.set(n,{resolve:s,reject:l}),window.parent.postMessage(i,"*");})}onMessage(e,t){return this.handlers.has(e)||this.handlers.set(e,new Set),this.handlers.get(e)?.add(t),()=>{let n=this.handlers.get(e);n&&(n.delete(t),n.size===0&&this.handlers.delete(e));}}destroy(){if(this.destroyed)return;this.destroyed=true,window.removeEventListener("message",this.listener);let e=new Error("Transport destroyed");for(let t of this.pending.values())t.reject(e);this.pending.clear(),this.handlers.clear();}handleMessage(e){if(this.destroyed)return;let t=e.data;if(!(!t||t.jsonrpc!=="2.0")){if("id"in t&&t.id&&!("method"in t)){let n=t,i=this.pending.get(n.id);if(!i)return;if(this.pending.delete(n.id),n.error){let s=new Error(n.error.message);s.code=n.error.code,s.data=n.error.data,i.reject(s);}else i.resolve(n.result);return}if("method"in t&&!("id"in t&&t.id)){let n=t,i=this.handlers.get(n.method);if(i)for(let s of i)s(n.params);}}}};function j(r){let{name:e,version:t,internal:n=false,forwardKeys:i}=r,s=new R,l=null,u={mode:"light",primaryColor:"#6366f1",tokens:{}},w=false,y=null,T=null,v=s.request("ui/initialize",{protocolVersion:"2026-01-26",clientInfo:{name:e,version:t},capabilities:{}}).then(o=>{l=E(o),u=l.theme,s.send("ui/notifications/initialized",{}),T=new k(s,i);}),d=s.onMessage("ui/notifications/host-context-changed",o=>{if(!o)return;let a=o.theme==="dark"?"dark":"light",p=o.tokens&&typeof o.tokens=="object"?o.tokens:u.tokens;u={mode:a,primaryColor:u.primaryColor,tokens:p};for(let g of m)g(u);}),f=s.onMessage("ui/themeChanged",o=>{if(!o)return;let a=o.mode==="dark"||o.mode==="light"?o.mode:u.mode,p=o.tokens&&typeof o.tokens=="object"?o.tokens:u.tokens;u={mode:a,primaryColor:u.primaryColor,tokens:p};for(let g of m)g(u);}),m=new Set,h=new Set,S=s.onMessage("ui/datachanged",o=>{if(!o)return;let a={source:"agent",server:o.server??"",tool:o.tool??""};for(let p of h)p(a);}),c=()=>l?.isNimbleBrain===true;return {get ready(){return v},get isNimbleBrainHost(){return c()},async callTool(o,a){let p={name:o,arguments:a??{}};n&&(p.server=e);let g=await s.request("tools/call",p);return M(g)},onDataChanged(o){return h.add(o),()=>{h.delete(o);}},getTheme(){return {...u}},onThemeChanged(o){return m.add(o),()=>{m.delete(o);}},action(o,a){c()&&s.send("ui/action",{action:o,...a});},chat(o,a){c()&&s.send("ui/chat",{message:o,context:a});},setVisibleState(o,a){c()&&(y&&clearTimeout(y),y=setTimeout(()=>{s.send("ui/stateChanged",{state:o,...a!==void 0&&{summary:a}}),y=null;},250));},downloadFile(o,a,p){if(!c())return;let g=typeof a=="string"?a:"[Blob content not serializable]";s.send("ui/downloadFile",{data:g,filename:o,mimeType:p??"application/octet-stream"});},openLink(o){c()?s.send("ui/openLink",{url:o}):window.open(o,"_blank","noopener");},_onMessage(o,a){return s.onMessage(o,a)},_request(o,a){return s.request(o,a)},destroy(){w||(w=true,y&&clearTimeout(y),T?.destroy(),d(),f(),S(),m.clear(),h.clear(),s.destroy());}}}function x(r,e){let t=structuredClone(e.initialState),n=new Set,i=false,s=null,l={};for(let d of Object.keys(e.actions))l[d]=f=>{i||(t=e.actions[d](t,f),u());};function u(){for(let d of n)d(t);e.visibleToAgent&&w(),e.persist&&y();}function w(){let d=e.summarize?.(t);r.setVisibleState(t,d);}function y(){s&&clearTimeout(s),s=setTimeout(()=>{r._request("ui/persistState",{state:t,version:e.version}).catch(()=>{}),s=null;},500);}let T;e.persist&&(T=r._onMessage("ui/stateLoaded",d=>{if(!d?.state)return;let f=d.state,m=d.version??1,h=e.version??1;if(e.migrations&&m<h){let S=m-1;for(let c=S;c<e.migrations.length;c++)f=e.migrations[c](f);}v.hydrate(f);}));let v={getState(){return t},subscribe(d){return n.add(d),()=>{n.delete(d);}},dispatch:l,hydrate(d){t=d;for(let f of n)f(t);},destroy(){i||(i=true,s&&clearTimeout(s),n.clear(),T?.());}};return v}window.NbSynapse={createSynapse:j,createStore:x};})();
@@ -0,0 +1,96 @@
1
+ interface SynapseOptions {
2
+ /** App name — must match the bundle name registered with the host */
3
+ name: string;
4
+ /** Semver version string */
5
+ version: string;
6
+ /**
7
+ * Mark as internal NimbleBrain app. Enables cross-server tool calls.
8
+ * External apps MUST NOT set this.
9
+ */
10
+ internal?: boolean;
11
+ /** Key combos to forward from iframe to host. Default: all Ctrl/Cmd combos + Escape. */
12
+ forwardKeys?: KeyForwardConfig[];
13
+ }
14
+ interface SynapseTheme {
15
+ mode: "light" | "dark";
16
+ primaryColor: string;
17
+ tokens: Record<string, string>;
18
+ }
19
+ interface DataChangedEvent {
20
+ source: "agent";
21
+ server: string;
22
+ tool: string;
23
+ }
24
+ interface ToolCallResult<T = unknown> {
25
+ data: T;
26
+ isError: boolean;
27
+ }
28
+ interface Synapse {
29
+ readonly ready: Promise<void>;
30
+ readonly isNimbleBrainHost: boolean;
31
+ callTool<TInput = Record<string, unknown>, TOutput = unknown>(name: string, args?: TInput): Promise<ToolCallResult<TOutput>>;
32
+ onDataChanged(callback: (event: DataChangedEvent) => void): () => void;
33
+ getTheme(): SynapseTheme;
34
+ onThemeChanged(callback: (theme: SynapseTheme) => void): () => void;
35
+ action(action: string, params?: Record<string, unknown>): void;
36
+ chat(message: string, context?: {
37
+ action?: string;
38
+ entity?: string;
39
+ }): void;
40
+ setVisibleState(state: Record<string, unknown>, summary?: string): void;
41
+ downloadFile(filename: string, content: string | Blob, mimeType?: string): void;
42
+ openLink(url: string): void;
43
+ /** @internal — used by createStore for ui/stateLoaded */
44
+ _onMessage(method: string, callback: (params: Record<string, unknown> | undefined) => void): () => void;
45
+ /** @internal — used by createStore for ui/persistState */
46
+ _request(method: string, params?: Record<string, unknown>): Promise<unknown>;
47
+ destroy(): void;
48
+ }
49
+ interface VisibleState {
50
+ state: Record<string, unknown>;
51
+ summary?: string;
52
+ }
53
+ interface StateAcknowledgement {
54
+ truncated: boolean;
55
+ }
56
+ type ActionReducer<TState, TPayload = unknown> = (state: TState, payload: TPayload) => TState;
57
+ interface StoreConfig<TState> {
58
+ initialState: TState;
59
+ actions: Record<string, ActionReducer<TState, any>>;
60
+ persist?: boolean;
61
+ visibleToAgent?: boolean;
62
+ summarize?: (state: TState) => string;
63
+ version?: number;
64
+ migrations?: Array<(oldState: any) => any>;
65
+ }
66
+ type StoreDispatch<TActions extends Record<string, ActionReducer<any, any>>> = {
67
+ [K in keyof TActions]: Parameters<TActions[K]>[1] extends undefined ? () => void : (payload: Parameters<TActions[K]>[1]) => void;
68
+ };
69
+ interface Store<TState, TActions extends Record<string, ActionReducer<TState, any>> = Record<string, ActionReducer<TState, any>>> {
70
+ getState(): TState;
71
+ subscribe(callback: (state: TState) => void): () => void;
72
+ dispatch: StoreDispatch<TActions>;
73
+ hydrate(state: TState): void;
74
+ destroy(): void;
75
+ }
76
+ interface KeyForwardConfig {
77
+ key: string;
78
+ ctrl?: boolean;
79
+ meta?: boolean;
80
+ shift?: boolean;
81
+ alt?: boolean;
82
+ }
83
+ interface ToolDefinition {
84
+ name: string;
85
+ description?: string;
86
+ inputSchema: Record<string, unknown>;
87
+ outputSchema?: Record<string, unknown>;
88
+ }
89
+ interface HostInfo {
90
+ isNimbleBrain: boolean;
91
+ serverName: string;
92
+ protocolVersion: string;
93
+ theme: SynapseTheme;
94
+ }
95
+
96
+ export type { ActionReducer as A, DataChangedEvent as D, HostInfo as H, KeyForwardConfig as K, SynapseOptions as S, ToolCallResult as T, VisibleState as V, Synapse as a, StoreConfig as b, Store as c, StateAcknowledgement as d, StoreDispatch as e, SynapseTheme as f, ToolDefinition as g };
@@ -0,0 +1,96 @@
1
+ interface SynapseOptions {
2
+ /** App name — must match the bundle name registered with the host */
3
+ name: string;
4
+ /** Semver version string */
5
+ version: string;
6
+ /**
7
+ * Mark as internal NimbleBrain app. Enables cross-server tool calls.
8
+ * External apps MUST NOT set this.
9
+ */
10
+ internal?: boolean;
11
+ /** Key combos to forward from iframe to host. Default: all Ctrl/Cmd combos + Escape. */
12
+ forwardKeys?: KeyForwardConfig[];
13
+ }
14
+ interface SynapseTheme {
15
+ mode: "light" | "dark";
16
+ primaryColor: string;
17
+ tokens: Record<string, string>;
18
+ }
19
+ interface DataChangedEvent {
20
+ source: "agent";
21
+ server: string;
22
+ tool: string;
23
+ }
24
+ interface ToolCallResult<T = unknown> {
25
+ data: T;
26
+ isError: boolean;
27
+ }
28
+ interface Synapse {
29
+ readonly ready: Promise<void>;
30
+ readonly isNimbleBrainHost: boolean;
31
+ callTool<TInput = Record<string, unknown>, TOutput = unknown>(name: string, args?: TInput): Promise<ToolCallResult<TOutput>>;
32
+ onDataChanged(callback: (event: DataChangedEvent) => void): () => void;
33
+ getTheme(): SynapseTheme;
34
+ onThemeChanged(callback: (theme: SynapseTheme) => void): () => void;
35
+ action(action: string, params?: Record<string, unknown>): void;
36
+ chat(message: string, context?: {
37
+ action?: string;
38
+ entity?: string;
39
+ }): void;
40
+ setVisibleState(state: Record<string, unknown>, summary?: string): void;
41
+ downloadFile(filename: string, content: string | Blob, mimeType?: string): void;
42
+ openLink(url: string): void;
43
+ /** @internal — used by createStore for ui/stateLoaded */
44
+ _onMessage(method: string, callback: (params: Record<string, unknown> | undefined) => void): () => void;
45
+ /** @internal — used by createStore for ui/persistState */
46
+ _request(method: string, params?: Record<string, unknown>): Promise<unknown>;
47
+ destroy(): void;
48
+ }
49
+ interface VisibleState {
50
+ state: Record<string, unknown>;
51
+ summary?: string;
52
+ }
53
+ interface StateAcknowledgement {
54
+ truncated: boolean;
55
+ }
56
+ type ActionReducer<TState, TPayload = unknown> = (state: TState, payload: TPayload) => TState;
57
+ interface StoreConfig<TState> {
58
+ initialState: TState;
59
+ actions: Record<string, ActionReducer<TState, any>>;
60
+ persist?: boolean;
61
+ visibleToAgent?: boolean;
62
+ summarize?: (state: TState) => string;
63
+ version?: number;
64
+ migrations?: Array<(oldState: any) => any>;
65
+ }
66
+ type StoreDispatch<TActions extends Record<string, ActionReducer<any, any>>> = {
67
+ [K in keyof TActions]: Parameters<TActions[K]>[1] extends undefined ? () => void : (payload: Parameters<TActions[K]>[1]) => void;
68
+ };
69
+ interface Store<TState, TActions extends Record<string, ActionReducer<TState, any>> = Record<string, ActionReducer<TState, any>>> {
70
+ getState(): TState;
71
+ subscribe(callback: (state: TState) => void): () => void;
72
+ dispatch: StoreDispatch<TActions>;
73
+ hydrate(state: TState): void;
74
+ destroy(): void;
75
+ }
76
+ interface KeyForwardConfig {
77
+ key: string;
78
+ ctrl?: boolean;
79
+ meta?: boolean;
80
+ shift?: boolean;
81
+ alt?: boolean;
82
+ }
83
+ interface ToolDefinition {
84
+ name: string;
85
+ description?: string;
86
+ inputSchema: Record<string, unknown>;
87
+ outputSchema?: Record<string, unknown>;
88
+ }
89
+ interface HostInfo {
90
+ isNimbleBrain: boolean;
91
+ serverName: string;
92
+ protocolVersion: string;
93
+ theme: SynapseTheme;
94
+ }
95
+
96
+ export type { ActionReducer as A, DataChangedEvent as D, HostInfo as H, KeyForwardConfig as K, SynapseOptions as S, ToolCallResult as T, VisibleState as V, Synapse as a, StoreConfig as b, Store as c, StateAcknowledgement as d, StoreDispatch as e, SynapseTheme as f, ToolDefinition as g };
@@ -0,0 +1,49 @@
1
+ 'use strict';
2
+
3
+ // src/vite/plugin.ts
4
+ function synapseVite(options) {
5
+ const { appName, platformUrl = "http://localhost:4321", injectBridge = true } = options;
6
+ return {
7
+ name: "synapse",
8
+ config() {
9
+ return {
10
+ define: {
11
+ "import.meta.env.SYNAPSE_PLATFORM_URL": JSON.stringify(platformUrl),
12
+ "import.meta.env.SYNAPSE_APP_NAME": JSON.stringify(appName)
13
+ },
14
+ server: {
15
+ hmr: {
16
+ // Required for HMR inside sandboxed iframe
17
+ protocol: "ws",
18
+ host: "localhost"
19
+ }
20
+ }
21
+ };
22
+ },
23
+ configureServer(server) {
24
+ server.middlewares.use((_req, res, next) => {
25
+ res.setHeader("Access-Control-Allow-Origin", "*");
26
+ res.setHeader("Access-Control-Allow-Methods", "*");
27
+ res.setHeader("Access-Control-Allow-Headers", "*");
28
+ next();
29
+ });
30
+ },
31
+ transformIndexHtml(html) {
32
+ if (!injectBridge) return html;
33
+ const bridgeScript = `
34
+ <script type="module">
35
+ import { createSynapse } from "@nimblebrain/synapse";
36
+ window.__synapse = createSynapse({
37
+ name: ${JSON.stringify(appName)},
38
+ version: "0.0.0-dev",
39
+ });
40
+ </script>`;
41
+ return html.replace("</head>", `${bridgeScript}
42
+ </head>`);
43
+ }
44
+ };
45
+ }
46
+
47
+ exports.synapseVite = synapseVite;
48
+ //# sourceMappingURL=index.cjs.map
49
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/vite/plugin.ts"],"names":[],"mappings":";;;AAmBO,SAAS,YAAY,OAAA,EAA2C;AACrE,EAAA,MAAM,EAAE,OAAA,EAAS,WAAA,GAAc,uBAAA,EAAyB,YAAA,GAAe,MAAK,GAAI,OAAA;AAEhF,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,SAAA;AAAA,IAEN,MAAA,GAAS;AACP,MAAA,OAAO;AAAA,QACL,MAAA,EAAQ;AAAA,UACN,sCAAA,EAAwC,IAAA,CAAK,SAAA,CAAU,WAAW,CAAA;AAAA,UAClE,kCAAA,EAAoC,IAAA,CAAK,SAAA,CAAU,OAAO;AAAA,SAC5D;AAAA,QACA,MAAA,EAAQ;AAAA,UACN,GAAA,EAAK;AAAA;AAAA,YAEH,QAAA,EAAU,IAAA;AAAA,YACV,IAAA,EAAM;AAAA;AACR;AACF,OACF;AAAA,IACF,CAAA;AAAA,IAEA,gBAAgB,MAAA,EAAuB;AAErC,MAAA,MAAA,CAAO,WAAA,CAAY,GAAA,CAAI,CAAC,IAAA,EAAM,KAAK,IAAA,KAAS;AAC1C,QAAA,GAAA,CAAI,SAAA,CAAU,+BAA+B,GAAG,CAAA;AAChD,QAAA,GAAA,CAAI,SAAA,CAAU,gCAAgC,GAAG,CAAA;AACjD,QAAA,GAAA,CAAI,SAAA,CAAU,gCAAgC,GAAG,CAAA;AACjD,QAAA,IAAA,EAAK;AAAA,MACP,CAAC,CAAA;AAAA,IACH,CAAA;AAAA,IAEA,mBAAmB,IAAA,EAAc;AAC/B,MAAA,IAAI,CAAC,cAAc,OAAO,IAAA;AAE1B,MAAA,MAAM,YAAA,GAAe;AAAA;AAAA;AAAA;AAAA,UAAA,EAIf,IAAA,CAAK,SAAA,CAAU,OAAO,CAAC,CAAA;AAAA;AAAA;AAAA,SAAA,CAAA;AAM7B,MAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,SAAA,EAAW,CAAA,EAAG,YAAY;AAAA,OAAA,CAAW,CAAA;AAAA,IAC3D;AAAA,GACF;AACF","file":"index.cjs","sourcesContent":["import type { Plugin, ViteDevServer } from \"vite\";\n\nexport interface SynapseVitePluginOptions {\n /** App name (must match manifest) */\n appName: string;\n /** Platform API URL (default: http://localhost:4321) */\n platformUrl?: string;\n /** Auto-inject bridge runtime into HTML entry (default: true) */\n injectBridge?: boolean;\n}\n\n/**\n * Vite plugin for Synapse app development.\n *\n * - Configures CORS for cross-origin iframe communication\n * - Injects ext-apps bridge runtime if `injectBridge` is true\n * - Sets up HMR WebSocket to work inside iframe sandbox\n * - Exposes platform URL as `import.meta.env.SYNAPSE_PLATFORM_URL`\n */\nexport function synapseVite(options: SynapseVitePluginOptions): Plugin {\n const { appName, platformUrl = \"http://localhost:4321\", injectBridge = true } = options;\n\n return {\n name: \"synapse\",\n\n config() {\n return {\n define: {\n \"import.meta.env.SYNAPSE_PLATFORM_URL\": JSON.stringify(platformUrl),\n \"import.meta.env.SYNAPSE_APP_NAME\": JSON.stringify(appName),\n },\n server: {\n hmr: {\n // Required for HMR inside sandboxed iframe\n protocol: \"ws\",\n host: \"localhost\",\n },\n },\n };\n },\n\n configureServer(server: ViteDevServer) {\n // Add CORS headers for cross-origin iframe communication\n server.middlewares.use((_req, res, next) => {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"*\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"*\");\n next();\n });\n },\n\n transformIndexHtml(html: string) {\n if (!injectBridge) return html;\n\n const bridgeScript = `\n<script type=\"module\">\n import { createSynapse } from \"@nimblebrain/synapse\";\n window.__synapse = createSynapse({\n name: ${JSON.stringify(appName)},\n version: \"0.0.0-dev\",\n });\n</script>`;\n\n // Inject before closing </head> tag\n return html.replace(\"</head>\", `${bridgeScript}\\n</head>`);\n },\n };\n}\n"]}
@@ -0,0 +1,21 @@
1
+ import { Plugin } from 'vite';
2
+
3
+ interface SynapseVitePluginOptions {
4
+ /** App name (must match manifest) */
5
+ appName: string;
6
+ /** Platform API URL (default: http://localhost:4321) */
7
+ platformUrl?: string;
8
+ /** Auto-inject bridge runtime into HTML entry (default: true) */
9
+ injectBridge?: boolean;
10
+ }
11
+ /**
12
+ * Vite plugin for Synapse app development.
13
+ *
14
+ * - Configures CORS for cross-origin iframe communication
15
+ * - Injects ext-apps bridge runtime if `injectBridge` is true
16
+ * - Sets up HMR WebSocket to work inside iframe sandbox
17
+ * - Exposes platform URL as `import.meta.env.SYNAPSE_PLATFORM_URL`
18
+ */
19
+ declare function synapseVite(options: SynapseVitePluginOptions): Plugin;
20
+
21
+ export { type SynapseVitePluginOptions, synapseVite };
@@ -0,0 +1,21 @@
1
+ import { Plugin } from 'vite';
2
+
3
+ interface SynapseVitePluginOptions {
4
+ /** App name (must match manifest) */
5
+ appName: string;
6
+ /** Platform API URL (default: http://localhost:4321) */
7
+ platformUrl?: string;
8
+ /** Auto-inject bridge runtime into HTML entry (default: true) */
9
+ injectBridge?: boolean;
10
+ }
11
+ /**
12
+ * Vite plugin for Synapse app development.
13
+ *
14
+ * - Configures CORS for cross-origin iframe communication
15
+ * - Injects ext-apps bridge runtime if `injectBridge` is true
16
+ * - Sets up HMR WebSocket to work inside iframe sandbox
17
+ * - Exposes platform URL as `import.meta.env.SYNAPSE_PLATFORM_URL`
18
+ */
19
+ declare function synapseVite(options: SynapseVitePluginOptions): Plugin;
20
+
21
+ export { type SynapseVitePluginOptions, synapseVite };
@@ -0,0 +1,47 @@
1
+ // src/vite/plugin.ts
2
+ function synapseVite(options) {
3
+ const { appName, platformUrl = "http://localhost:4321", injectBridge = true } = options;
4
+ return {
5
+ name: "synapse",
6
+ config() {
7
+ return {
8
+ define: {
9
+ "import.meta.env.SYNAPSE_PLATFORM_URL": JSON.stringify(platformUrl),
10
+ "import.meta.env.SYNAPSE_APP_NAME": JSON.stringify(appName)
11
+ },
12
+ server: {
13
+ hmr: {
14
+ // Required for HMR inside sandboxed iframe
15
+ protocol: "ws",
16
+ host: "localhost"
17
+ }
18
+ }
19
+ };
20
+ },
21
+ configureServer(server) {
22
+ server.middlewares.use((_req, res, next) => {
23
+ res.setHeader("Access-Control-Allow-Origin", "*");
24
+ res.setHeader("Access-Control-Allow-Methods", "*");
25
+ res.setHeader("Access-Control-Allow-Headers", "*");
26
+ next();
27
+ });
28
+ },
29
+ transformIndexHtml(html) {
30
+ if (!injectBridge) return html;
31
+ const bridgeScript = `
32
+ <script type="module">
33
+ import { createSynapse } from "@nimblebrain/synapse";
34
+ window.__synapse = createSynapse({
35
+ name: ${JSON.stringify(appName)},
36
+ version: "0.0.0-dev",
37
+ });
38
+ </script>`;
39
+ return html.replace("</head>", `${bridgeScript}
40
+ </head>`);
41
+ }
42
+ };
43
+ }
44
+
45
+ export { synapseVite };
46
+ //# sourceMappingURL=index.js.map
47
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/vite/plugin.ts"],"names":[],"mappings":";AAmBO,SAAS,YAAY,OAAA,EAA2C;AACrE,EAAA,MAAM,EAAE,OAAA,EAAS,WAAA,GAAc,uBAAA,EAAyB,YAAA,GAAe,MAAK,GAAI,OAAA;AAEhF,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,SAAA;AAAA,IAEN,MAAA,GAAS;AACP,MAAA,OAAO;AAAA,QACL,MAAA,EAAQ;AAAA,UACN,sCAAA,EAAwC,IAAA,CAAK,SAAA,CAAU,WAAW,CAAA;AAAA,UAClE,kCAAA,EAAoC,IAAA,CAAK,SAAA,CAAU,OAAO;AAAA,SAC5D;AAAA,QACA,MAAA,EAAQ;AAAA,UACN,GAAA,EAAK;AAAA;AAAA,YAEH,QAAA,EAAU,IAAA;AAAA,YACV,IAAA,EAAM;AAAA;AACR;AACF,OACF;AAAA,IACF,CAAA;AAAA,IAEA,gBAAgB,MAAA,EAAuB;AAErC,MAAA,MAAA,CAAO,WAAA,CAAY,GAAA,CAAI,CAAC,IAAA,EAAM,KAAK,IAAA,KAAS;AAC1C,QAAA,GAAA,CAAI,SAAA,CAAU,+BAA+B,GAAG,CAAA;AAChD,QAAA,GAAA,CAAI,SAAA,CAAU,gCAAgC,GAAG,CAAA;AACjD,QAAA,GAAA,CAAI,SAAA,CAAU,gCAAgC,GAAG,CAAA;AACjD,QAAA,IAAA,EAAK;AAAA,MACP,CAAC,CAAA;AAAA,IACH,CAAA;AAAA,IAEA,mBAAmB,IAAA,EAAc;AAC/B,MAAA,IAAI,CAAC,cAAc,OAAO,IAAA;AAE1B,MAAA,MAAM,YAAA,GAAe;AAAA;AAAA;AAAA;AAAA,UAAA,EAIf,IAAA,CAAK,SAAA,CAAU,OAAO,CAAC,CAAA;AAAA;AAAA;AAAA,SAAA,CAAA;AAM7B,MAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,SAAA,EAAW,CAAA,EAAG,YAAY;AAAA,OAAA,CAAW,CAAA;AAAA,IAC3D;AAAA,GACF;AACF","file":"index.js","sourcesContent":["import type { Plugin, ViteDevServer } from \"vite\";\n\nexport interface SynapseVitePluginOptions {\n /** App name (must match manifest) */\n appName: string;\n /** Platform API URL (default: http://localhost:4321) */\n platformUrl?: string;\n /** Auto-inject bridge runtime into HTML entry (default: true) */\n injectBridge?: boolean;\n}\n\n/**\n * Vite plugin for Synapse app development.\n *\n * - Configures CORS for cross-origin iframe communication\n * - Injects ext-apps bridge runtime if `injectBridge` is true\n * - Sets up HMR WebSocket to work inside iframe sandbox\n * - Exposes platform URL as `import.meta.env.SYNAPSE_PLATFORM_URL`\n */\nexport function synapseVite(options: SynapseVitePluginOptions): Plugin {\n const { appName, platformUrl = \"http://localhost:4321\", injectBridge = true } = options;\n\n return {\n name: \"synapse\",\n\n config() {\n return {\n define: {\n \"import.meta.env.SYNAPSE_PLATFORM_URL\": JSON.stringify(platformUrl),\n \"import.meta.env.SYNAPSE_APP_NAME\": JSON.stringify(appName),\n },\n server: {\n hmr: {\n // Required for HMR inside sandboxed iframe\n protocol: \"ws\",\n host: \"localhost\",\n },\n },\n };\n },\n\n configureServer(server: ViteDevServer) {\n // Add CORS headers for cross-origin iframe communication\n server.middlewares.use((_req, res, next) => {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"*\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"*\");\n next();\n });\n },\n\n transformIndexHtml(html: string) {\n if (!injectBridge) return html;\n\n const bridgeScript = `\n<script type=\"module\">\n import { createSynapse } from \"@nimblebrain/synapse\";\n window.__synapse = createSynapse({\n name: ${JSON.stringify(appName)},\n version: \"0.0.0-dev\",\n });\n</script>`;\n\n // Inject before closing </head> tag\n return html.replace(\"</head>\", `${bridgeScript}\\n</head>`);\n },\n };\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,89 @@
1
+ {
2
+ "name": "@nimblebrain/synapse",
3
+ "version": "0.1.0",
4
+ "description": "Agent-aware app SDK for the NimbleBrain platform",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/index.js",
10
+ "require": "./dist/index.cjs"
11
+ },
12
+ "./react": {
13
+ "types": "./dist/react/index.d.ts",
14
+ "import": "./dist/react/index.js",
15
+ "require": "./dist/react/index.cjs"
16
+ },
17
+ "./vite": {
18
+ "types": "./dist/vite/index.d.ts",
19
+ "import": "./dist/vite/index.js",
20
+ "require": "./dist/vite/index.cjs"
21
+ },
22
+ "./codegen": {
23
+ "types": "./dist/codegen/index.d.ts",
24
+ "import": "./dist/codegen/index.js",
25
+ "require": "./dist/codegen/index.cjs"
26
+ }
27
+ },
28
+ "bin": {
29
+ "synapse": "./dist/codegen/cli.js"
30
+ },
31
+ "files": [
32
+ "dist"
33
+ ],
34
+ "scripts": {
35
+ "build": "tsup",
36
+ "test": "vitest run",
37
+ "test:watch": "vitest",
38
+ "typecheck": "tsc --noEmit",
39
+ "lint": "biome check .",
40
+ "lint:fix": "biome check --write .",
41
+ "format": "biome format --write .",
42
+ "ci": "npm run lint && npm run typecheck && npm run build && npm run test",
43
+ "prepublishOnly": "npm run build"
44
+ },
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "git+https://github.com/NimbleBrainInc/synapse.git"
48
+ },
49
+ "homepage": "https://github.com/NimbleBrainInc/synapse#readme",
50
+ "bugs": {
51
+ "url": "https://github.com/NimbleBrainInc/synapse/issues"
52
+ },
53
+ "author": "NimbleBrain Inc.",
54
+ "peerDependencies": {
55
+ "@modelcontextprotocol/ext-apps": "^1.3.1",
56
+ "react": "^18.0.0 || ^19.0.0"
57
+ },
58
+ "peerDependenciesMeta": {
59
+ "react": {
60
+ "optional": true
61
+ }
62
+ },
63
+ "devDependencies": {
64
+ "@biomejs/biome": "^2.4.9",
65
+ "@modelcontextprotocol/ext-apps": "^1.3.1",
66
+ "@testing-library/react": "^16.0.0",
67
+ "@types/node": "^22.19.15",
68
+ "@types/react": "^19.0.0",
69
+ "happy-dom": "^15.0.0",
70
+ "react": "^19.0.0",
71
+ "tsup": "^8.0.0",
72
+ "typescript": "^5.7.0",
73
+ "vite": "^6.0.0",
74
+ "vitest": "^3.0.0"
75
+ },
76
+ "license": "MIT",
77
+ "keywords": [
78
+ "nimblebrain",
79
+ "synapse",
80
+ "mcp",
81
+ "ext-apps",
82
+ "agent",
83
+ "sdk",
84
+ "model-context-protocol",
85
+ "ai",
86
+ "react",
87
+ "vite"
88
+ ]
89
+ }