@nimblebrain/synapse 0.2.1 → 0.2.2
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/{chunk-Y4ZDNAYQ.cjs → chunk-MQNKIR7K.cjs} +31 -8
- package/dist/chunk-MQNKIR7K.cjs.map +1 -0
- package/dist/{chunk-7KEYXJWD.js → chunk-QY4IBJKV.js} +31 -8
- package/dist/chunk-QY4IBJKV.js.map +1 -0
- package/dist/codegen/cli.cjs +1 -1
- package/dist/codegen/cli.js +1 -1
- package/dist/codegen/index.d.cts +1 -1
- package/dist/codegen/index.d.ts +1 -1
- package/dist/index.cjs +4 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +30 -2
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +7 -2
- package/dist/react/index.d.ts +7 -2
- package/dist/react/index.js +29 -2
- package/dist/react/index.js.map +1 -1
- package/dist/{server-NNW54YW5.js → server-7BRGSPT3.js} +13 -13
- package/dist/{server-NNW54YW5.js.map → server-7BRGSPT3.js.map} +1 -1
- package/dist/{server-3BDZ5S72.cjs → server-SRE7E3G3.cjs} +13 -13
- package/dist/{server-3BDZ5S72.cjs.map → server-SRE7E3G3.cjs.map} +1 -1
- package/dist/synapse-runtime.iife.global.js +1 -1
- package/dist/{types-DElq_otH.d.cts → types-CG7zrCn-.d.cts} +31 -3
- package/dist/{types-DElq_otH.d.ts → types-CG7zrCn-.d.ts} +31 -3
- package/dist/vite/index.cjs +6 -6
- package/dist/vite/index.cjs.map +1 -1
- package/dist/vite/index.js +6 -6
- package/dist/vite/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-7KEYXJWD.js.map +0 -1
- package/dist/chunk-Y4ZDNAYQ.cjs.map +0 -1
|
@@ -68,6 +68,22 @@ interface ToolCallResult<T = unknown> {
|
|
|
68
68
|
data: T;
|
|
69
69
|
isError: boolean;
|
|
70
70
|
}
|
|
71
|
+
/** Result from a file picker request */
|
|
72
|
+
interface FileResult {
|
|
73
|
+
filename: string;
|
|
74
|
+
mimeType: string;
|
|
75
|
+
size: number;
|
|
76
|
+
base64Data: string;
|
|
77
|
+
}
|
|
78
|
+
/** Options for requesting a file from the user */
|
|
79
|
+
interface RequestFileOptions {
|
|
80
|
+
/** File type filter (e.g., ".csv,.json", "image/*") */
|
|
81
|
+
accept?: string;
|
|
82
|
+
/** Max file size in bytes. Default: 25 MB */
|
|
83
|
+
maxSize?: number;
|
|
84
|
+
/** Allow multiple file selection. Default: false */
|
|
85
|
+
multiple?: boolean;
|
|
86
|
+
}
|
|
71
87
|
interface Synapse {
|
|
72
88
|
readonly ready: Promise<void>;
|
|
73
89
|
readonly isNimbleBrainHost: boolean;
|
|
@@ -107,9 +123,21 @@ interface Synapse {
|
|
|
107
123
|
setVisibleState(state: Record<string, unknown>, summary?: string): void;
|
|
108
124
|
downloadFile(filename: string, content: string | Blob, mimeType?: string): void;
|
|
109
125
|
openLink(url: string): void;
|
|
110
|
-
/**
|
|
126
|
+
/**
|
|
127
|
+
* Request a file from the user via the host's native file picker.
|
|
128
|
+
* NimbleBrain-only: throws in non-NimbleBrain hosts.
|
|
129
|
+
* Returns null if the user cancels.
|
|
130
|
+
*/
|
|
131
|
+
requestFile(options?: RequestFileOptions): Promise<FileResult | null>;
|
|
132
|
+
/**
|
|
133
|
+
* Request multiple files from the user.
|
|
134
|
+
* NimbleBrain-only: throws in non-NimbleBrain hosts.
|
|
135
|
+
* Returns empty array if the user cancels.
|
|
136
|
+
*/
|
|
137
|
+
requestFiles(options?: RequestFileOptions): Promise<FileResult[]>;
|
|
138
|
+
/** @internal — used by createStore for synapse/state-loaded */
|
|
111
139
|
_onMessage(method: string, callback: (params: Record<string, unknown> | undefined) => void): () => void;
|
|
112
|
-
/** @internal — used by createStore for
|
|
140
|
+
/** @internal — used by createStore for synapse/persist-state */
|
|
113
141
|
_request(method: string, params?: Record<string, unknown>): Promise<unknown>;
|
|
114
142
|
/** True after destroy() has been called. */
|
|
115
143
|
readonly destroyed: boolean;
|
|
@@ -162,4 +190,4 @@ interface HostInfo {
|
|
|
162
190
|
theme: SynapseTheme;
|
|
163
191
|
}
|
|
164
192
|
|
|
165
|
-
export type { ActionReducer as A, BuiltinActionType as B, DataChangedEvent as D, HostInfo as H, KeyForwardConfig as K, NavigatePayload as N, SynapseOptions as S, ToolCallResult as T, VisibleState as V, Synapse as a, StoreConfig as b, Store as c, AgentAction as d, NotifyPayload as e, StateAcknowledgement as f, StoreDispatch as g, SynapseTheme as h, ToolDefinition as i };
|
|
193
|
+
export type { ActionReducer as A, BuiltinActionType as B, DataChangedEvent as D, FileResult as F, HostInfo as H, KeyForwardConfig as K, NavigatePayload as N, RequestFileOptions as R, SynapseOptions as S, ToolCallResult as T, VisibleState as V, Synapse as a, StoreConfig as b, Store as c, AgentAction as d, NotifyPayload as e, StateAcknowledgement as f, StoreDispatch as g, SynapseTheme as h, ToolDefinition as i };
|
package/dist/vite/index.cjs
CHANGED
|
@@ -259,7 +259,7 @@ function previewHostHtml(appName) {
|
|
|
259
259
|
// Only for mutating operations (not list/search/get which are read-only)
|
|
260
260
|
var tn = msg.params.name || "";
|
|
261
261
|
if (!response.error && !tn.startsWith("list_") && !tn.startsWith("search_") && !tn.startsWith("get_") && !tn.startsWith("query_")) {
|
|
262
|
-
post({jsonrpc:"2.0",method:"
|
|
262
|
+
post({jsonrpc:"2.0",method:"synapse/data-changed",params:{tool:tn,server:"preview"}});
|
|
263
263
|
}
|
|
264
264
|
} catch(err) {
|
|
265
265
|
post({jsonrpc:"2.0",id:originalId,error:{code:-32000,message:err.message}});
|
|
@@ -268,17 +268,17 @@ function previewHostHtml(appName) {
|
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
// Log other messages
|
|
271
|
-
if (msg.method === "
|
|
272
|
-
else if (msg.method === "
|
|
273
|
-
else if (msg.method === "ui/
|
|
274
|
-
else if (msg.method === "
|
|
271
|
+
if (msg.method === "synapse/chat") console.log("[chat]", msg.params?.message);
|
|
272
|
+
else if (msg.method === "synapse/action") console.log("[action]", msg.params?.action, msg.params);
|
|
273
|
+
else if (msg.method === "ui/update-model-context") { console.log("[model-context]", msg.params?.structuredContent); if (msg.id) post({jsonrpc:"2.0",id:msg.id,result:{}}); }
|
|
274
|
+
else if (msg.method === "synapse/keydown") { /* ignore */ }
|
|
275
275
|
else if (msg.method) console.log("[bridge]", msg.method, msg);
|
|
276
276
|
});
|
|
277
277
|
|
|
278
278
|
document.getElementById("toggle").onclick = function() {
|
|
279
279
|
dark = !dark;
|
|
280
280
|
document.body.style.background = dark ? "#0f172a" : "#f1f5f9";
|
|
281
|
-
post({jsonrpc:"2.0",method:"
|
|
281
|
+
post({jsonrpc:"2.0",method:"synapse/theme-changed",params:{mode:dark?"dark":"light",tokens:getTokens(dark)}});
|
|
282
282
|
};
|
|
283
283
|
|
|
284
284
|
// Load the iframe AFTER the message listener is attached to avoid
|
package/dist/vite/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/vite/plugin.ts"],"names":["resolve","existsSync","readFileSync","join","spawn"],"mappings":";;;;;;;AAiDO,SAAS,WAAA,CAAY,OAAA,GAAoC,EAAC,EAAW;AAC1E,EAAA,MAAM,aAAA,GAAgB,QAAQ,OAAA,KAAY,KAAA;AAC1C,EAAA,IAAI,QAAA,GAA4B,IAAA;AAChC,EAAA,IAAI,OAAA,GAAU,QAAQ,OAAA,IAAW,KAAA;AACjC,EAAA,IAAI,aAAA,GAAqC,IAAA;AACzC,EAAA,MAAM,eAAA,uBAAsB,GAAA,EAG1B;AACF,EAAA,IAAI,YAAA,GAAe,EAAA;AAEnB,EAAA,SAAS,aAAa,IAAA,EAA+B;AACnD,IAAA,MAAM,YAAA,GAAe,OAAA,CAAQ,QAAA,GACzBA,YAAA,CAAQ,OAAA,CAAQ,QAAQ,CAAA,GACxBA,YAAA,CAAQ,IAAA,EAAM,IAAA,EAAM,eAAe,CAAA;AAEvC,IAAA,IAAI,CAACC,aAAA,CAAW,YAAY,CAAA,EAAG,OAAO,IAAA;AACtC,IAAA,IAAI;AACF,MAAA,OAAO,IAAA,CAAK,KAAA,CAAMC,eAAA,CAAa,YAAA,EAAc,OAAO,CAAC,CAAA;AAAA,IACvD,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AAEA,EAAA,SAAS,eAAA,CAAgB,GAAa,IAAA,EAA6B;AACjE,IAAA,IAAI,OAAA,CAAQ,SAAA,EAAW,OAAO,OAAA,CAAQ,SAAA;AACtC,IAAA,MAAM,GAAA,GAAM,EAAE,MAAA,EAAQ,UAAA;AACtB,IAAA,IAAI,CAAC,GAAA,EAAK,OAAA,EAAS,OAAO,IAAA;AAE1B,IAAA,MAAM,SAAA,GAAYF,YAAA,CAAQ,IAAA,EAAM,IAAI,CAAA;AACpC,IAAA,IAAI,MAAM,GAAA,CAAI,OAAA;AACd,IAAA,MAAM,IAAA,GAAO,GAAA,CAAI,IAAA,IAAQ,EAAC;AAG1B,IAAA,IAAI,QAAQ,QAAA,IAAYC,aAAA,CAAWE,UAAK,SAAA,EAAW,gBAAgB,CAAC,CAAA,EAAG;AACrE,MAAA,GAAA,GAAM,eAAA;AAAA,IACR;AAEA,IAAA,OAAO,CAAA,GAAA,EAAM,IAAA,CAAK,SAAA,CAAU,SAAS,CAAC,CAAA,IAAA,EAAO,GAAG,CAAA,CAAA,EAAI,IAAA,CAAK,IAAA,CAAK,GAAG,CAAC,CAAA,CAAA;AAAA,EACpE;AAEA,EAAA,SAAS,YAAY,GAAA,EAAmB;AACtC,IAAA,aAAA,GAAgBC,oBAAM,GAAA,EAAK;AAAA,MACzB,KAAA,EAAO,IAAA;AAAA,MACP,KAAA,EAAO,CAAC,MAAA,EAAQ,MAAA,EAAQ,MAAM;AAAA,KAC/B,CAAA;AAED,IAAA,aAAA,CAAc,MAAA,EAAQ,EAAA,CAAG,MAAA,EAAQ,CAAC,CAAA,KAAc;AAC9C,MAAA,OAAA,CAAQ,MAAA,CAAO,KAAA,CAAM,CAAA,QAAA,EAAW,CAAC,CAAA,CAAE,CAAA;AAAA,IACrC,CAAC,CAAA;AAED,IAAA,aAAA,CAAc,MAAA,EAAQ,EAAA,CAAG,MAAA,EAAQ,CAAC,CAAA,KAAc;AAC9C,MAAA,YAAA,IAAgB,EAAE,QAAA,EAAS;AAE3B,MAAA,MAAM,KAAA,GAAQ,YAAA,CAAa,KAAA,CAAM,IAAI,CAAA;AACrC,MAAA,YAAA,GAAe,KAAA,CAAM,KAAI,IAAK,EAAA;AAC9B,MAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,QAAA,IAAI,CAAC,IAAA,CAAK,IAAA,EAAK,EAAG;AAClB,QAAA,IAAI;AACF,UAAA,MAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA;AAC3B,UAAA,IAAI,IAAI,EAAA,IAAM,eAAA,CAAgB,GAAA,CAAI,GAAA,CAAI,EAAE,CAAA,EAAG;AACzC,YAAA,MAAM,CAAA,GAAI,eAAA,CAAgB,GAAA,CAAI,GAAA,CAAI,EAAE,CAAA;AACpC,YAAA,eAAA,CAAgB,MAAA,CAAO,IAAI,EAAE,CAAA;AAC7B,YAAA,CAAA,EAAG,QAAQ,GAAG,CAAA;AAAA,UAChB;AAAA,QACF,CAAA,CAAA,MAAQ;AAEN,UAAA,OAAA,CAAQ,MAAA,CAAO,KAAA,CAAM,CAAA,QAAA,EAAW,IAAI;AAAA,CAAI,CAAA;AAAA,QAC1C;AAAA,MACF;AAAA,IACF,CAAC,CAAA;AAED,IAAA,aAAA,CAAc,EAAA,CAAG,MAAA,EAAQ,CAAC,IAAA,KAAS;AACjC,MAAA,IAAI,IAAA,KAAS,IAAA,IAAQ,IAAA,KAAS,CAAA,EAAG;AAC/B,QAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,gCAAA,EAAmC,IAAI,CAAA,CAAE,CAAA;AAAA,MACzD;AACA,MAAA,aAAA,GAAgB,IAAA;AAAA,IAClB,CAAC,CAAA;AAGD,IAAA,YAAA,CAAa;AAAA,MACX,OAAA,EAAS,KAAA;AAAA,MACT,EAAA,EAAI,QAAA;AAAA,MACJ,MAAA,EAAQ,YAAA;AAAA,MACR,MAAA,EAAQ;AAAA,QACN,eAAA,EAAiB,YAAA;AAAA,QACjB,cAAc,EAAC;AAAA,QACf,UAAA,EAAY,EAAE,IAAA,EAAM,iBAAA,EAAmB,SAAS,OAAA;AAAQ;AAC1D,KACD,CAAA;AAAA,EACH;AAEA,EAAA,SAAS,aAAa,GAAA,EAAoC;AACxD,IAAA,IAAI,CAAC,aAAA,EAAe,KAAA,EAAO,QAAA,EAAU;AACrC,IAAA,aAAA,CAAc,MAAM,KAAA,CAAM,CAAA,EAAG,IAAA,CAAK,SAAA,CAAU,GAAG,CAAC;AAAA,CAAI,CAAA;AAAA,EACtD;AAEA,EAAA,SAAS,cAAA,CAAe,MAAc,IAAA,EAAiD;AACrF,IAAA,OAAO,IAAI,OAAA,CAAQ,CAACJ,QAAAA,EAAS,MAAA,KAAW;AACtC,MAAA,MAAM,EAAA,GAAK,CAAA,QAAA,EAAW,IAAA,CAAK,GAAA,EAAK,CAAA,CAAA,EAAI,IAAA,CAAK,MAAA,EAAO,CAAE,SAAS,EAAE,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAA,CAAA;AAC1E,MAAA,eAAA,CAAgB,IAAI,EAAA,EAAI,EAAE,OAAA,EAAAA,QAAAA,EAAS,QAAQ,CAAA;AAC3C,MAAA,YAAA,CAAa;AAAA,QACX,OAAA,EAAS,KAAA;AAAA,QACT,EAAA;AAAA,QACA,MAAA,EAAQ,YAAA;AAAA,QACR,MAAA,EAAQ,EAAE,IAAA,EAAM,SAAA,EAAW,IAAA;AAAK,OACjC,CAAA;AACD,MAAA,UAAA,CAAW,MAAM;AACf,QAAA,IAAI,eAAA,CAAgB,GAAA,CAAI,EAAE,CAAA,EAAG;AAC3B,UAAA,eAAA,CAAgB,OAAO,EAAE,CAAA;AACzB,UAAA,MAAA,CAAO,IAAI,KAAA,CAAM,2BAA2B,CAAC,CAAA;AAAA,QAC/C;AAAA,MACF,GAAG,GAAK,CAAA;AAAA,IACV,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,SAAA;AAAA,IAEN,MAAA,GAAS;AACP,MAAA,OAAO;AAAA,QACL,MAAA,EAAQ;AAAA,UACN,kCAAA,EAAoC,IAAA,CAAK,SAAA,CAAU,OAAO;AAAA,SAC5D;AAAA,QACA,MAAA,EAAQ;AAAA,UACN,GAAA,EAAK;AAAA,YACH,QAAA,EAAU,IAAA;AAAA,YACV,IAAA,EAAM;AAAA;AACR;AACF,OACF;AAAA,IACF,CAAA;AAAA,IAEA,eAAe,MAAA,EAAQ;AACrB,MAAA,QAAA,GAAW,YAAA,CAAa,OAAO,IAAI,CAAA;AACnC,MAAA,IAAI,UAAU,IAAA,EAAM;AAClB,QAAA,OAAA,GAAU,OAAA,CAAQ,WAAW,QAAA,CAAS,IAAA;AAAA,MACxC;AAAA,IACF,CAAA;AAAA,IAEA,gBAAgB,MAAA,EAAuB;AAErC,MAAA,IAAI,iBAAiB,QAAA,EAAU;AAC7B,QAAA,MAAM,GAAA,GAAM,eAAA,CAAgB,QAAA,EAAU,MAAA,CAAO,OAAO,IAAI,CAAA;AACxD,QAAA,IAAI,GAAA,EAAK;AACP,UAAA,OAAA,CAAQ,GAAA,CAAI;AAAA,iCAAA,EAAsC,GAAG;AAAA,CAAI,CAAA;AACzD,UAAA,WAAA,CAAY,GAAG,CAAA;AAAA,QACjB;AAAA,MACF;AAEA,MAAA,MAAA,CAAO,WAAA,CAAY,GAAA,CAAI,CAAC,GAAA,EAAK,KAAK,IAAA,KAAS;AAEzC,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;AAGjD,QAAA,IAAI,GAAA,CAAI,GAAA,KAAQ,YAAA,IAAgB,GAAA,CAAI,QAAQ,aAAA,EAAe;AACzD,UAAA,GAAA,CAAI,SAAA,CAAU,GAAA,EAAK,EAAE,cAAA,EAAgB,aAAa,CAAA;AAClD,UAAA,GAAA,CAAI,GAAA,CAAI,eAAA,CAAgB,OAAO,CAAC,CAAA;AAChC,UAAA;AAAA,QACF;AAGA,QAAA,IAAI,GAAA,CAAI,MAAA,KAAW,MAAA,IAAU,GAAA,CAAI,QAAQ,QAAA,EAAU;AACjD,UAAA,IAAI,IAAA,GAAO,EAAA;AACX,UAAA,GAAA,CAAI,EAAA,CAAG,MAAA,EAAQ,CAAC,KAAA,KAAkB;AAChC,YAAA,IAAA,IAAQ,MAAM,QAAA,EAAS;AAAA,UACzB,CAAC,CAAA;AACD,UAAA,GAAA,CAAI,EAAA,CAAG,OAAO,YAAY;AACxB,YAAA,IAAI;AACF,cAAA,MAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA;AAC3B,cAAA,MAAM,MAAA,GAAS,MAAM,cAAA,CAAe,GAAA,CAAI,MAAA,CAAO,MAAM,GAAA,CAAI,MAAA,CAAO,SAAA,IAAa,EAAE,CAAA;AAC/E,cAAA,GAAA,CAAI,SAAA,CAAU,GAAA,EAAK,EAAE,cAAA,EAAgB,oBAAoB,CAAA;AACzD,cAAA,GAAA,CAAI,GAAA,CAAI,IAAA,CAAK,SAAA,CAAU,MAAM,CAAC,CAAA;AAAA,YAChC,SAAS,GAAA,EAAK;AACZ,cAAA,GAAA,CAAI,SAAA,CAAU,GAAA,EAAK,EAAE,cAAA,EAAgB,oBAAoB,CAAA;AACzD,cAAA,GAAA,CAAI,GAAA;AAAA,gBACF,KAAK,SAAA,CAAU;AAAA,kBACb,OAAA,EAAS,KAAA;AAAA,kBACT,EAAA,EAAI,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA,CAAE,EAAA;AAAA,kBACrB,OAAO,EAAE,IAAA,EAAM,KAAA,EAAQ,OAAA,EAAU,IAAc,OAAA;AAAQ,iBACxD;AAAA,eACH;AAAA,YACF;AAAA,UACF,CAAC,CAAA;AACD,UAAA;AAAA,QACF;AAEA,QAAA,IAAA,EAAK;AAAA,MACP,CAAC,CAAA;AAAA,IACH,CAAA;AAAA,IAEA,QAAA,GAAW;AAET,MAAA,IAAI,aAAA,EAAe;AACjB,QAAA,aAAA,CAAc,KAAK,SAAS,CAAA;AAC5B,QAAA,aAAA,GAAgB,IAAA;AAAA,MAClB;AAAA,IACF;AAAA,GACF;AACF;AAEA,SAAS,gBAAgB,OAAA,EAAyB;AAChD,EAAA,OAAO,CAAA;AAAA;AAAA;AAAA;AAAA,SAAA,EAIE,OAAO,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAA,EAgBO,OAAO,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAAA,CAAA;AA2FhC","file":"index.cjs","sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport type { Plugin, ViteDevServer } from \"vite\";\n\nexport interface SynapseVitePluginOptions {\n /** App name. If omitted, reads from ../manifest.json */\n appName?: string;\n /** Path to manifest.json. Default: ../manifest.json (relative to ui/) */\n manifest?: string;\n /**\n * Shell command to start the MCP server. If omitted, derived from manifest.\n * The server runs in stdio mode — stdin/stdout JSON-RPC.\n */\n serverCmd?: string;\n /** Set to false to disable the preview host page at /__preview */\n preview?: boolean;\n}\n\ninterface Manifest {\n name: string;\n version?: string;\n server?: {\n type?: string;\n entry_point?: string;\n mcp_config?: {\n command?: string;\n args?: string[];\n };\n };\n}\n\n/**\n * Synapse Vite plugin — full local dev experience for MCP apps.\n *\n * What it does:\n * - Reads ../manifest.json to get app name and server config\n * - Spawns the MCP server as a child process (stdio mode)\n * - Serves a preview host page at /__preview that iframes your app\n * - Proxies tool calls from the iframe through POST /__mcp to the server\n * - Handles the ext-apps handshake so Synapse hooks work\n * - HMR works inside the iframe — edit .tsx, see changes instantly\n *\n * Usage in vite.config.ts:\n * import { synapseVite } from \"@nimblebrain/synapse/vite\";\n * export default { plugins: [react(), viteSingleFile(), synapseVite()] };\n *\n * Then: cd ui && npm run dev && open http://localhost:5173/__preview\n */\nexport function synapseVite(options: SynapseVitePluginOptions = {}): Plugin {\n const enablePreview = options.preview !== false;\n let manifest: Manifest | null = null;\n let appName = options.appName ?? \"app\";\n let serverProcess: ChildProcess | null = null;\n const pendingRequests = new Map<\n string,\n { resolve: (v: unknown) => void; reject: (e: Error) => void }\n >();\n let serverBuffer = \"\";\n\n function loadManifest(root: string): Manifest | null {\n const manifestPath = options.manifest\n ? resolve(options.manifest)\n : resolve(root, \"..\", \"manifest.json\");\n\n if (!existsSync(manifestPath)) return null;\n try {\n return JSON.parse(readFileSync(manifestPath, \"utf-8\"));\n } catch {\n return null;\n }\n }\n\n function deriveServerCmd(m: Manifest, root: string): string | null {\n if (options.serverCmd) return options.serverCmd;\n const cfg = m.server?.mcp_config;\n if (!cfg?.command) return null;\n\n const serverDir = resolve(root, \"..\");\n let cmd = cfg.command;\n const args = cfg.args ?? [];\n\n // Python projects: use `uv run` if pyproject.toml exists\n if (cmd === \"python\" && existsSync(join(serverDir, \"pyproject.toml\"))) {\n cmd = \"uv run python\";\n }\n\n return `cd ${JSON.stringify(serverDir)} && ${cmd} ${args.join(\" \")}`;\n }\n\n function startServer(cmd: string): void {\n serverProcess = spawn(cmd, {\n shell: true,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n serverProcess.stderr?.on(\"data\", (d: Buffer) => {\n process.stderr.write(` [mcp] ${d}`);\n });\n\n serverProcess.stdout?.on(\"data\", (d: Buffer) => {\n serverBuffer += d.toString();\n // Parse line-delimited JSON-RPC responses\n const lines = serverBuffer.split(\"\\n\");\n serverBuffer = lines.pop() ?? \"\"; // keep incomplete line\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n const msg = JSON.parse(line);\n if (msg.id && pendingRequests.has(msg.id)) {\n const p = pendingRequests.get(msg.id);\n pendingRequests.delete(msg.id);\n p?.resolve(msg);\n }\n } catch {\n // Not JSON — log it\n process.stderr.write(` [mcp] ${line}\\n`);\n }\n }\n });\n\n serverProcess.on(\"exit\", (code) => {\n if (code !== null && code !== 0) {\n console.error(` [mcp] Server exited with code ${code}`);\n }\n serverProcess = null;\n });\n\n // Send initialize\n sendToServer({\n jsonrpc: \"2.0\",\n id: \"init-1\",\n method: \"initialize\",\n params: {\n protocolVersion: \"2024-11-05\",\n capabilities: {},\n clientInfo: { name: \"synapse-preview\", version: \"0.1.0\" },\n },\n });\n }\n\n function sendToServer(msg: Record<string, unknown>): void {\n if (!serverProcess?.stdin?.writable) return;\n serverProcess.stdin.write(`${JSON.stringify(msg)}\\n`);\n }\n\n function callServerTool(name: string, args: Record<string, unknown>): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const id = `preview-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n pendingRequests.set(id, { resolve, reject });\n sendToServer({\n jsonrpc: \"2.0\",\n id,\n method: \"tools/call\",\n params: { name, arguments: args },\n });\n setTimeout(() => {\n if (pendingRequests.has(id)) {\n pendingRequests.delete(id);\n reject(new Error(\"Tool call timed out (10s)\"));\n }\n }, 10000);\n });\n }\n\n return {\n name: \"synapse\",\n\n config() {\n return {\n define: {\n \"import.meta.env.SYNAPSE_APP_NAME\": JSON.stringify(appName),\n },\n server: {\n hmr: {\n protocol: \"ws\",\n host: \"localhost\",\n },\n },\n };\n },\n\n configResolved(config) {\n manifest = loadManifest(config.root);\n if (manifest?.name) {\n appName = options.appName ?? manifest.name;\n }\n },\n\n configureServer(server: ViteDevServer) {\n // Start MCP server\n if (enablePreview && manifest) {\n const cmd = deriveServerCmd(manifest, server.config.root);\n if (cmd) {\n console.log(`\\n [synapse] Starting MCP server: ${cmd}\\n`);\n startServer(cmd);\n }\n }\n\n server.middlewares.use((req, res, next) => {\n // CORS for iframe communication\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"*\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"*\");\n\n // /__preview — bridge host page\n if (req.url === \"/__preview\" || req.url === \"/__preview/\") {\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(previewHostHtml(appName));\n return;\n }\n\n // POST /__mcp — tool call proxy\n if (req.method === \"POST\" && req.url === \"/__mcp\") {\n let body = \"\";\n req.on(\"data\", (chunk: Buffer) => {\n body += chunk.toString();\n });\n req.on(\"end\", async () => {\n try {\n const msg = JSON.parse(body);\n const result = await callServerTool(msg.params.name, msg.params.arguments || {});\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(result));\n } catch (err) {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n jsonrpc: \"2.0\",\n id: JSON.parse(body).id,\n error: { code: -32000, message: (err as Error).message },\n }),\n );\n }\n });\n return;\n }\n\n next();\n });\n },\n\n buildEnd() {\n // Kill server on build end (for production builds)\n if (serverProcess) {\n serverProcess.kill(\"SIGTERM\");\n serverProcess = null;\n }\n },\n };\n}\n\nfunction previewHostHtml(appName: string): string {\n return `<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\" />\n <title>${appName} — Synapse Preview</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; background: #0f172a; color: #e2e8f0; }\n header { padding: 10px 16px; background: #1e293b; border-bottom: 1px solid #334155; display: flex; align-items: center; gap: 10px; font-size: 13px; }\n header .dot { width: 8px; height: 8px; border-radius: 50%; background: #22c55e; }\n header .name { font-weight: 600; }\n header .spacer { flex: 1; }\n header button { background: #334155; border: none; color: #e2e8f0; padding: 3px 8px; border-radius: 4px; cursor: pointer; font-size: 12px; }\n header .url { color: #64748b; font-size: 11px; font-family: monospace; }\n iframe { width: 100%; height: calc(100vh - 41px); border: none; }\n </style>\n</head>\n<body>\n <header>\n <span class=\"dot\"></span>\n <span class=\"name\">${appName}</span>\n <span class=\"spacer\"></span>\n <button id=\"toggle\">Toggle Theme</button>\n <span class=\"url\">Synapse Preview</span>\n </header>\n <iframe id=\"app\"></iframe>\n\n <script>\n var iframe = document.getElementById(\"app\");\n var dark = true;\n\n function getTokens(d) {\n return d ? {\n \"--color-background-primary\":\"#0f172a\",\"--color-text-primary\":\"#e2e8f0\",\n \"--color-background-secondary\":\"#1e293b\",\"--color-text-primary\":\"#e2e8f0\",\n \"--color-text-accent\":\"#6366f1\",\"--nb-color-accent-foreground\":\"#fff\",\n \"--color-text-secondary\":\"#94a3b8\",\"--color-border-primary\":\"#334155\",\n \"--color-ring-primary\":\"#6366f1\",\"--nb-color-danger\":\"#ef4444\",\n \"--border-radius-sm\":\"0.5rem\",\"--font-sans\":\"-apple-system,BlinkMacSystemFont,sans-serif\"\n } : {\n \"--color-background-primary\":\"#ffffff\",\"--color-text-primary\":\"#0f172a\",\n \"--color-background-secondary\":\"#f8fafc\",\"--color-text-primary\":\"#0f172a\",\n \"--color-text-accent\":\"#6366f1\",\"--nb-color-accent-foreground\":\"#fff\",\n \"--color-text-secondary\":\"#64748b\",\"--color-border-primary\":\"#e2e8f0\",\n \"--color-ring-primary\":\"#6366f1\",\"--nb-color-danger\":\"#ef4444\",\n \"--border-radius-sm\":\"0.5rem\",\"--font-sans\":\"-apple-system,BlinkMacSystemFont,sans-serif\"\n };\n }\n\n function post(msg) { iframe.contentWindow.postMessage(msg, \"*\"); }\n\n window.addEventListener(\"message\", async function(e) {\n if (e.source !== iframe.contentWindow) return;\n var msg = e.data;\n if (!msg || typeof msg !== \"object\") return;\n\n // ext-apps handshake\n if (msg.method === \"ui/initialize\" && msg.id) {\n post({ jsonrpc:\"2.0\", id:msg.id, result: {\n protocolVersion:\"2026-01-26\",\n serverInfo:{name:\"nimblebrain\",version:\"preview\"},\n capabilities:{openLinks:{},serverTools:{}},\n hostContext:{theme:dark?\"dark\":\"light\",primaryColor:\"#6366f1\",tokens:getTokens(dark)}\n }});\n return;\n }\n if (msg.method === \"ui/notifications/initialized\") return;\n\n // Tool calls — proxy via Vite middleware\n if (msg.method === \"tools/call\" && msg.id) {\n var originalId = msg.id;\n try {\n var r = await fetch(\"/__mcp\", {\n method:\"POST\", headers:{\"Content-Type\":\"application/json\"},\n body: JSON.stringify({jsonrpc:\"2.0\",id:msg.id,method:\"tools/call\",params:{name:msg.params.name,arguments:msg.params.arguments||{}}})\n });\n var response = await r.json();\n response.id = originalId;\n post(response);\n // Notify the app that data changed so hooks auto-refresh\n // Only for mutating operations (not list/search/get which are read-only)\n var tn = msg.params.name || \"\";\n if (!response.error && !tn.startsWith(\"list_\") && !tn.startsWith(\"search_\") && !tn.startsWith(\"get_\") && !tn.startsWith(\"query_\")) {\n post({jsonrpc:\"2.0\",method:\"ui/datachanged\",params:{tool:tn,server:\"preview\"}});\n }\n } catch(err) {\n post({jsonrpc:\"2.0\",id:originalId,error:{code:-32000,message:err.message}});\n }\n return;\n }\n\n // Log other messages\n if (msg.method === \"ui/chat\") console.log(\"[chat]\", msg.params?.message);\n else if (msg.method === \"ui/action\") console.log(\"[action]\", msg.params?.action, msg.params);\n else if (msg.method === \"ui/stateChanged\") { console.log(\"[state]\", msg.params?.state); post({jsonrpc:\"2.0\",method:\"ui/stateAcknowledged\",params:{truncated:false}}); }\n else if (msg.method === \"ui/keydown\") { /* ignore */ }\n else if (msg.method) console.log(\"[bridge]\", msg.method, msg);\n });\n\n document.getElementById(\"toggle\").onclick = function() {\n dark = !dark;\n document.body.style.background = dark ? \"#0f172a\" : \"#f1f5f9\";\n post({jsonrpc:\"2.0\",method:\"ui/themeChanged\",params:{mode:dark?\"dark\":\"light\",tokens:getTokens(dark)}});\n };\n\n // Load the iframe AFTER the message listener is attached to avoid\n // a race where the app sends ui/initialize before the bridge is ready.\n iframe.src = \"/\";\n </script>\n</body>\n</html>`;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/vite/plugin.ts"],"names":["resolve","existsSync","readFileSync","join","spawn"],"mappings":"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","file":"index.cjs","sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport type { Plugin, ViteDevServer } from \"vite\";\n\nexport interface SynapseVitePluginOptions {\n /** App name. If omitted, reads from ../manifest.json */\n appName?: string;\n /** Path to manifest.json. Default: ../manifest.json (relative to ui/) */\n manifest?: string;\n /**\n * Shell command to start the MCP server. If omitted, derived from manifest.\n * The server runs in stdio mode — stdin/stdout JSON-RPC.\n */\n serverCmd?: string;\n /** Set to false to disable the preview host page at /__preview */\n preview?: boolean;\n}\n\ninterface Manifest {\n name: string;\n version?: string;\n server?: {\n type?: string;\n entry_point?: string;\n mcp_config?: {\n command?: string;\n args?: string[];\n };\n };\n}\n\n/**\n * Synapse Vite plugin — full local dev experience for MCP apps.\n *\n * What it does:\n * - Reads ../manifest.json to get app name and server config\n * - Spawns the MCP server as a child process (stdio mode)\n * - Serves a preview host page at /__preview that iframes your app\n * - Proxies tool calls from the iframe through POST /__mcp to the server\n * - Handles the ext-apps handshake so Synapse hooks work\n * - HMR works inside the iframe — edit .tsx, see changes instantly\n *\n * Usage in vite.config.ts:\n * import { synapseVite } from \"@nimblebrain/synapse/vite\";\n * export default { plugins: [react(), viteSingleFile(), synapseVite()] };\n *\n * Then: cd ui && npm run dev && open http://localhost:5173/__preview\n */\nexport function synapseVite(options: SynapseVitePluginOptions = {}): Plugin {\n const enablePreview = options.preview !== false;\n let manifest: Manifest | null = null;\n let appName = options.appName ?? \"app\";\n let serverProcess: ChildProcess | null = null;\n const pendingRequests = new Map<\n string,\n { resolve: (v: unknown) => void; reject: (e: Error) => void }\n >();\n let serverBuffer = \"\";\n\n function loadManifest(root: string): Manifest | null {\n const manifestPath = options.manifest\n ? resolve(options.manifest)\n : resolve(root, \"..\", \"manifest.json\");\n\n if (!existsSync(manifestPath)) return null;\n try {\n return JSON.parse(readFileSync(manifestPath, \"utf-8\"));\n } catch {\n return null;\n }\n }\n\n function deriveServerCmd(m: Manifest, root: string): string | null {\n if (options.serverCmd) return options.serverCmd;\n const cfg = m.server?.mcp_config;\n if (!cfg?.command) return null;\n\n const serverDir = resolve(root, \"..\");\n let cmd = cfg.command;\n const args = cfg.args ?? [];\n\n // Python projects: use `uv run` if pyproject.toml exists\n if (cmd === \"python\" && existsSync(join(serverDir, \"pyproject.toml\"))) {\n cmd = \"uv run python\";\n }\n\n return `cd ${JSON.stringify(serverDir)} && ${cmd} ${args.join(\" \")}`;\n }\n\n function startServer(cmd: string): void {\n serverProcess = spawn(cmd, {\n shell: true,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n serverProcess.stderr?.on(\"data\", (d: Buffer) => {\n process.stderr.write(` [mcp] ${d}`);\n });\n\n serverProcess.stdout?.on(\"data\", (d: Buffer) => {\n serverBuffer += d.toString();\n // Parse line-delimited JSON-RPC responses\n const lines = serverBuffer.split(\"\\n\");\n serverBuffer = lines.pop() ?? \"\"; // keep incomplete line\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n const msg = JSON.parse(line);\n if (msg.id && pendingRequests.has(msg.id)) {\n const p = pendingRequests.get(msg.id);\n pendingRequests.delete(msg.id);\n p?.resolve(msg);\n }\n } catch {\n // Not JSON — log it\n process.stderr.write(` [mcp] ${line}\\n`);\n }\n }\n });\n\n serverProcess.on(\"exit\", (code) => {\n if (code !== null && code !== 0) {\n console.error(` [mcp] Server exited with code ${code}`);\n }\n serverProcess = null;\n });\n\n // Send initialize\n sendToServer({\n jsonrpc: \"2.0\",\n id: \"init-1\",\n method: \"initialize\",\n params: {\n protocolVersion: \"2024-11-05\",\n capabilities: {},\n clientInfo: { name: \"synapse-preview\", version: \"0.1.0\" },\n },\n });\n }\n\n function sendToServer(msg: Record<string, unknown>): void {\n if (!serverProcess?.stdin?.writable) return;\n serverProcess.stdin.write(`${JSON.stringify(msg)}\\n`);\n }\n\n function callServerTool(name: string, args: Record<string, unknown>): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const id = `preview-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n pendingRequests.set(id, { resolve, reject });\n sendToServer({\n jsonrpc: \"2.0\",\n id,\n method: \"tools/call\",\n params: { name, arguments: args },\n });\n setTimeout(() => {\n if (pendingRequests.has(id)) {\n pendingRequests.delete(id);\n reject(new Error(\"Tool call timed out (10s)\"));\n }\n }, 10000);\n });\n }\n\n return {\n name: \"synapse\",\n\n config() {\n return {\n define: {\n \"import.meta.env.SYNAPSE_APP_NAME\": JSON.stringify(appName),\n },\n server: {\n hmr: {\n protocol: \"ws\",\n host: \"localhost\",\n },\n },\n };\n },\n\n configResolved(config) {\n manifest = loadManifest(config.root);\n if (manifest?.name) {\n appName = options.appName ?? manifest.name;\n }\n },\n\n configureServer(server: ViteDevServer) {\n // Start MCP server\n if (enablePreview && manifest) {\n const cmd = deriveServerCmd(manifest, server.config.root);\n if (cmd) {\n console.log(`\\n [synapse] Starting MCP server: ${cmd}\\n`);\n startServer(cmd);\n }\n }\n\n server.middlewares.use((req, res, next) => {\n // CORS for iframe communication\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"*\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"*\");\n\n // /__preview — bridge host page\n if (req.url === \"/__preview\" || req.url === \"/__preview/\") {\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(previewHostHtml(appName));\n return;\n }\n\n // POST /__mcp — tool call proxy\n if (req.method === \"POST\" && req.url === \"/__mcp\") {\n let body = \"\";\n req.on(\"data\", (chunk: Buffer) => {\n body += chunk.toString();\n });\n req.on(\"end\", async () => {\n try {\n const msg = JSON.parse(body);\n const result = await callServerTool(msg.params.name, msg.params.arguments || {});\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(result));\n } catch (err) {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n jsonrpc: \"2.0\",\n id: JSON.parse(body).id,\n error: { code: -32000, message: (err as Error).message },\n }),\n );\n }\n });\n return;\n }\n\n next();\n });\n },\n\n buildEnd() {\n // Kill server on build end (for production builds)\n if (serverProcess) {\n serverProcess.kill(\"SIGTERM\");\n serverProcess = null;\n }\n },\n };\n}\n\nfunction previewHostHtml(appName: string): string {\n return `<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\" />\n <title>${appName} — Synapse Preview</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; background: #0f172a; color: #e2e8f0; }\n header { padding: 10px 16px; background: #1e293b; border-bottom: 1px solid #334155; display: flex; align-items: center; gap: 10px; font-size: 13px; }\n header .dot { width: 8px; height: 8px; border-radius: 50%; background: #22c55e; }\n header .name { font-weight: 600; }\n header .spacer { flex: 1; }\n header button { background: #334155; border: none; color: #e2e8f0; padding: 3px 8px; border-radius: 4px; cursor: pointer; font-size: 12px; }\n header .url { color: #64748b; font-size: 11px; font-family: monospace; }\n iframe { width: 100%; height: calc(100vh - 41px); border: none; }\n </style>\n</head>\n<body>\n <header>\n <span class=\"dot\"></span>\n <span class=\"name\">${appName}</span>\n <span class=\"spacer\"></span>\n <button id=\"toggle\">Toggle Theme</button>\n <span class=\"url\">Synapse Preview</span>\n </header>\n <iframe id=\"app\"></iframe>\n\n <script>\n var iframe = document.getElementById(\"app\");\n var dark = true;\n\n function getTokens(d) {\n return d ? {\n \"--color-background-primary\":\"#0f172a\",\"--color-text-primary\":\"#e2e8f0\",\n \"--color-background-secondary\":\"#1e293b\",\"--color-text-primary\":\"#e2e8f0\",\n \"--color-text-accent\":\"#6366f1\",\"--nb-color-accent-foreground\":\"#fff\",\n \"--color-text-secondary\":\"#94a3b8\",\"--color-border-primary\":\"#334155\",\n \"--color-ring-primary\":\"#6366f1\",\"--nb-color-danger\":\"#ef4444\",\n \"--border-radius-sm\":\"0.5rem\",\"--font-sans\":\"-apple-system,BlinkMacSystemFont,sans-serif\"\n } : {\n \"--color-background-primary\":\"#ffffff\",\"--color-text-primary\":\"#0f172a\",\n \"--color-background-secondary\":\"#f8fafc\",\"--color-text-primary\":\"#0f172a\",\n \"--color-text-accent\":\"#6366f1\",\"--nb-color-accent-foreground\":\"#fff\",\n \"--color-text-secondary\":\"#64748b\",\"--color-border-primary\":\"#e2e8f0\",\n \"--color-ring-primary\":\"#6366f1\",\"--nb-color-danger\":\"#ef4444\",\n \"--border-radius-sm\":\"0.5rem\",\"--font-sans\":\"-apple-system,BlinkMacSystemFont,sans-serif\"\n };\n }\n\n function post(msg) { iframe.contentWindow.postMessage(msg, \"*\"); }\n\n window.addEventListener(\"message\", async function(e) {\n if (e.source !== iframe.contentWindow) return;\n var msg = e.data;\n if (!msg || typeof msg !== \"object\") return;\n\n // ext-apps handshake\n if (msg.method === \"ui/initialize\" && msg.id) {\n post({ jsonrpc:\"2.0\", id:msg.id, result: {\n protocolVersion:\"2026-01-26\",\n serverInfo:{name:\"nimblebrain\",version:\"preview\"},\n capabilities:{openLinks:{},serverTools:{}},\n hostContext:{theme:dark?\"dark\":\"light\",primaryColor:\"#6366f1\",tokens:getTokens(dark)}\n }});\n return;\n }\n if (msg.method === \"ui/notifications/initialized\") return;\n\n // Tool calls — proxy via Vite middleware\n if (msg.method === \"tools/call\" && msg.id) {\n var originalId = msg.id;\n try {\n var r = await fetch(\"/__mcp\", {\n method:\"POST\", headers:{\"Content-Type\":\"application/json\"},\n body: JSON.stringify({jsonrpc:\"2.0\",id:msg.id,method:\"tools/call\",params:{name:msg.params.name,arguments:msg.params.arguments||{}}})\n });\n var response = await r.json();\n response.id = originalId;\n post(response);\n // Notify the app that data changed so hooks auto-refresh\n // Only for mutating operations (not list/search/get which are read-only)\n var tn = msg.params.name || \"\";\n if (!response.error && !tn.startsWith(\"list_\") && !tn.startsWith(\"search_\") && !tn.startsWith(\"get_\") && !tn.startsWith(\"query_\")) {\n post({jsonrpc:\"2.0\",method:\"synapse/data-changed\",params:{tool:tn,server:\"preview\"}});\n }\n } catch(err) {\n post({jsonrpc:\"2.0\",id:originalId,error:{code:-32000,message:err.message}});\n }\n return;\n }\n\n // Log other messages\n if (msg.method === \"synapse/chat\") console.log(\"[chat]\", msg.params?.message);\n else if (msg.method === \"synapse/action\") console.log(\"[action]\", msg.params?.action, msg.params);\n else if (msg.method === \"ui/update-model-context\") { console.log(\"[model-context]\", msg.params?.structuredContent); if (msg.id) post({jsonrpc:\"2.0\",id:msg.id,result:{}}); }\n else if (msg.method === \"synapse/keydown\") { /* ignore */ }\n else if (msg.method) console.log(\"[bridge]\", msg.method, msg);\n });\n\n document.getElementById(\"toggle\").onclick = function() {\n dark = !dark;\n document.body.style.background = dark ? \"#0f172a\" : \"#f1f5f9\";\n post({jsonrpc:\"2.0\",method:\"synapse/theme-changed\",params:{mode:dark?\"dark\":\"light\",tokens:getTokens(dark)}});\n };\n\n // Load the iframe AFTER the message listener is attached to avoid\n // a race where the app sends ui/initialize before the bridge is ready.\n iframe.src = \"/\";\n </script>\n</body>\n</html>`;\n}\n"]}
|
package/dist/vite/index.js
CHANGED
|
@@ -257,7 +257,7 @@ function previewHostHtml(appName) {
|
|
|
257
257
|
// Only for mutating operations (not list/search/get which are read-only)
|
|
258
258
|
var tn = msg.params.name || "";
|
|
259
259
|
if (!response.error && !tn.startsWith("list_") && !tn.startsWith("search_") && !tn.startsWith("get_") && !tn.startsWith("query_")) {
|
|
260
|
-
post({jsonrpc:"2.0",method:"
|
|
260
|
+
post({jsonrpc:"2.0",method:"synapse/data-changed",params:{tool:tn,server:"preview"}});
|
|
261
261
|
}
|
|
262
262
|
} catch(err) {
|
|
263
263
|
post({jsonrpc:"2.0",id:originalId,error:{code:-32000,message:err.message}});
|
|
@@ -266,17 +266,17 @@ function previewHostHtml(appName) {
|
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
// Log other messages
|
|
269
|
-
if (msg.method === "
|
|
270
|
-
else if (msg.method === "
|
|
271
|
-
else if (msg.method === "ui/
|
|
272
|
-
else if (msg.method === "
|
|
269
|
+
if (msg.method === "synapse/chat") console.log("[chat]", msg.params?.message);
|
|
270
|
+
else if (msg.method === "synapse/action") console.log("[action]", msg.params?.action, msg.params);
|
|
271
|
+
else if (msg.method === "ui/update-model-context") { console.log("[model-context]", msg.params?.structuredContent); if (msg.id) post({jsonrpc:"2.0",id:msg.id,result:{}}); }
|
|
272
|
+
else if (msg.method === "synapse/keydown") { /* ignore */ }
|
|
273
273
|
else if (msg.method) console.log("[bridge]", msg.method, msg);
|
|
274
274
|
});
|
|
275
275
|
|
|
276
276
|
document.getElementById("toggle").onclick = function() {
|
|
277
277
|
dark = !dark;
|
|
278
278
|
document.body.style.background = dark ? "#0f172a" : "#f1f5f9";
|
|
279
|
-
post({jsonrpc:"2.0",method:"
|
|
279
|
+
post({jsonrpc:"2.0",method:"synapse/theme-changed",params:{mode:dark?"dark":"light",tokens:getTokens(dark)}});
|
|
280
280
|
};
|
|
281
281
|
|
|
282
282
|
// Load the iframe AFTER the message listener is attached to avoid
|
package/dist/vite/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/vite/plugin.ts"],"names":["resolve"],"mappings":"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","file":"index.js","sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport type { Plugin, ViteDevServer } from \"vite\";\n\nexport interface SynapseVitePluginOptions {\n /** App name. If omitted, reads from ../manifest.json */\n appName?: string;\n /** Path to manifest.json. Default: ../manifest.json (relative to ui/) */\n manifest?: string;\n /**\n * Shell command to start the MCP server. If omitted, derived from manifest.\n * The server runs in stdio mode — stdin/stdout JSON-RPC.\n */\n serverCmd?: string;\n /** Set to false to disable the preview host page at /__preview */\n preview?: boolean;\n}\n\ninterface Manifest {\n name: string;\n version?: string;\n server?: {\n type?: string;\n entry_point?: string;\n mcp_config?: {\n command?: string;\n args?: string[];\n };\n };\n}\n\n/**\n * Synapse Vite plugin — full local dev experience for MCP apps.\n *\n * What it does:\n * - Reads ../manifest.json to get app name and server config\n * - Spawns the MCP server as a child process (stdio mode)\n * - Serves a preview host page at /__preview that iframes your app\n * - Proxies tool calls from the iframe through POST /__mcp to the server\n * - Handles the ext-apps handshake so Synapse hooks work\n * - HMR works inside the iframe — edit .tsx, see changes instantly\n *\n * Usage in vite.config.ts:\n * import { synapseVite } from \"@nimblebrain/synapse/vite\";\n * export default { plugins: [react(), viteSingleFile(), synapseVite()] };\n *\n * Then: cd ui && npm run dev && open http://localhost:5173/__preview\n */\nexport function synapseVite(options: SynapseVitePluginOptions = {}): Plugin {\n const enablePreview = options.preview !== false;\n let manifest: Manifest | null = null;\n let appName = options.appName ?? \"app\";\n let serverProcess: ChildProcess | null = null;\n const pendingRequests = new Map<\n string,\n { resolve: (v: unknown) => void; reject: (e: Error) => void }\n >();\n let serverBuffer = \"\";\n\n function loadManifest(root: string): Manifest | null {\n const manifestPath = options.manifest\n ? resolve(options.manifest)\n : resolve(root, \"..\", \"manifest.json\");\n\n if (!existsSync(manifestPath)) return null;\n try {\n return JSON.parse(readFileSync(manifestPath, \"utf-8\"));\n } catch {\n return null;\n }\n }\n\n function deriveServerCmd(m: Manifest, root: string): string | null {\n if (options.serverCmd) return options.serverCmd;\n const cfg = m.server?.mcp_config;\n if (!cfg?.command) return null;\n\n const serverDir = resolve(root, \"..\");\n let cmd = cfg.command;\n const args = cfg.args ?? [];\n\n // Python projects: use `uv run` if pyproject.toml exists\n if (cmd === \"python\" && existsSync(join(serverDir, \"pyproject.toml\"))) {\n cmd = \"uv run python\";\n }\n\n return `cd ${JSON.stringify(serverDir)} && ${cmd} ${args.join(\" \")}`;\n }\n\n function startServer(cmd: string): void {\n serverProcess = spawn(cmd, {\n shell: true,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n serverProcess.stderr?.on(\"data\", (d: Buffer) => {\n process.stderr.write(` [mcp] ${d}`);\n });\n\n serverProcess.stdout?.on(\"data\", (d: Buffer) => {\n serverBuffer += d.toString();\n // Parse line-delimited JSON-RPC responses\n const lines = serverBuffer.split(\"\\n\");\n serverBuffer = lines.pop() ?? \"\"; // keep incomplete line\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n const msg = JSON.parse(line);\n if (msg.id && pendingRequests.has(msg.id)) {\n const p = pendingRequests.get(msg.id);\n pendingRequests.delete(msg.id);\n p?.resolve(msg);\n }\n } catch {\n // Not JSON — log it\n process.stderr.write(` [mcp] ${line}\\n`);\n }\n }\n });\n\n serverProcess.on(\"exit\", (code) => {\n if (code !== null && code !== 0) {\n console.error(` [mcp] Server exited with code ${code}`);\n }\n serverProcess = null;\n });\n\n // Send initialize\n sendToServer({\n jsonrpc: \"2.0\",\n id: \"init-1\",\n method: \"initialize\",\n params: {\n protocolVersion: \"2024-11-05\",\n capabilities: {},\n clientInfo: { name: \"synapse-preview\", version: \"0.1.0\" },\n },\n });\n }\n\n function sendToServer(msg: Record<string, unknown>): void {\n if (!serverProcess?.stdin?.writable) return;\n serverProcess.stdin.write(`${JSON.stringify(msg)}\\n`);\n }\n\n function callServerTool(name: string, args: Record<string, unknown>): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const id = `preview-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n pendingRequests.set(id, { resolve, reject });\n sendToServer({\n jsonrpc: \"2.0\",\n id,\n method: \"tools/call\",\n params: { name, arguments: args },\n });\n setTimeout(() => {\n if (pendingRequests.has(id)) {\n pendingRequests.delete(id);\n reject(new Error(\"Tool call timed out (10s)\"));\n }\n }, 10000);\n });\n }\n\n return {\n name: \"synapse\",\n\n config() {\n return {\n define: {\n \"import.meta.env.SYNAPSE_APP_NAME\": JSON.stringify(appName),\n },\n server: {\n hmr: {\n protocol: \"ws\",\n host: \"localhost\",\n },\n },\n };\n },\n\n configResolved(config) {\n manifest = loadManifest(config.root);\n if (manifest?.name) {\n appName = options.appName ?? manifest.name;\n }\n },\n\n configureServer(server: ViteDevServer) {\n // Start MCP server\n if (enablePreview && manifest) {\n const cmd = deriveServerCmd(manifest, server.config.root);\n if (cmd) {\n console.log(`\\n [synapse] Starting MCP server: ${cmd}\\n`);\n startServer(cmd);\n }\n }\n\n server.middlewares.use((req, res, next) => {\n // CORS for iframe communication\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"*\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"*\");\n\n // /__preview — bridge host page\n if (req.url === \"/__preview\" || req.url === \"/__preview/\") {\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(previewHostHtml(appName));\n return;\n }\n\n // POST /__mcp — tool call proxy\n if (req.method === \"POST\" && req.url === \"/__mcp\") {\n let body = \"\";\n req.on(\"data\", (chunk: Buffer) => {\n body += chunk.toString();\n });\n req.on(\"end\", async () => {\n try {\n const msg = JSON.parse(body);\n const result = await callServerTool(msg.params.name, msg.params.arguments || {});\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(result));\n } catch (err) {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n jsonrpc: \"2.0\",\n id: JSON.parse(body).id,\n error: { code: -32000, message: (err as Error).message },\n }),\n );\n }\n });\n return;\n }\n\n next();\n });\n },\n\n buildEnd() {\n // Kill server on build end (for production builds)\n if (serverProcess) {\n serverProcess.kill(\"SIGTERM\");\n serverProcess = null;\n }\n },\n };\n}\n\nfunction previewHostHtml(appName: string): string {\n return `<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\" />\n <title>${appName} — Synapse Preview</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; background: #0f172a; color: #e2e8f0; }\n header { padding: 10px 16px; background: #1e293b; border-bottom: 1px solid #334155; display: flex; align-items: center; gap: 10px; font-size: 13px; }\n header .dot { width: 8px; height: 8px; border-radius: 50%; background: #22c55e; }\n header .name { font-weight: 600; }\n header .spacer { flex: 1; }\n header button { background: #334155; border: none; color: #e2e8f0; padding: 3px 8px; border-radius: 4px; cursor: pointer; font-size: 12px; }\n header .url { color: #64748b; font-size: 11px; font-family: monospace; }\n iframe { width: 100%; height: calc(100vh - 41px); border: none; }\n </style>\n</head>\n<body>\n <header>\n <span class=\"dot\"></span>\n <span class=\"name\">${appName}</span>\n <span class=\"spacer\"></span>\n <button id=\"toggle\">Toggle Theme</button>\n <span class=\"url\">Synapse Preview</span>\n </header>\n <iframe id=\"app\"></iframe>\n\n <script>\n var iframe = document.getElementById(\"app\");\n var dark = true;\n\n function getTokens(d) {\n return d ? {\n \"--color-background-primary\":\"#0f172a\",\"--color-text-primary\":\"#e2e8f0\",\n \"--color-background-secondary\":\"#1e293b\",\"--color-text-primary\":\"#e2e8f0\",\n \"--color-text-accent\":\"#6366f1\",\"--nb-color-accent-foreground\":\"#fff\",\n \"--color-text-secondary\":\"#94a3b8\",\"--color-border-primary\":\"#334155\",\n \"--color-ring-primary\":\"#6366f1\",\"--nb-color-danger\":\"#ef4444\",\n \"--border-radius-sm\":\"0.5rem\",\"--font-sans\":\"-apple-system,BlinkMacSystemFont,sans-serif\"\n } : {\n \"--color-background-primary\":\"#ffffff\",\"--color-text-primary\":\"#0f172a\",\n \"--color-background-secondary\":\"#f8fafc\",\"--color-text-primary\":\"#0f172a\",\n \"--color-text-accent\":\"#6366f1\",\"--nb-color-accent-foreground\":\"#fff\",\n \"--color-text-secondary\":\"#64748b\",\"--color-border-primary\":\"#e2e8f0\",\n \"--color-ring-primary\":\"#6366f1\",\"--nb-color-danger\":\"#ef4444\",\n \"--border-radius-sm\":\"0.5rem\",\"--font-sans\":\"-apple-system,BlinkMacSystemFont,sans-serif\"\n };\n }\n\n function post(msg) { iframe.contentWindow.postMessage(msg, \"*\"); }\n\n window.addEventListener(\"message\", async function(e) {\n if (e.source !== iframe.contentWindow) return;\n var msg = e.data;\n if (!msg || typeof msg !== \"object\") return;\n\n // ext-apps handshake\n if (msg.method === \"ui/initialize\" && msg.id) {\n post({ jsonrpc:\"2.0\", id:msg.id, result: {\n protocolVersion:\"2026-01-26\",\n serverInfo:{name:\"nimblebrain\",version:\"preview\"},\n capabilities:{openLinks:{},serverTools:{}},\n hostContext:{theme:dark?\"dark\":\"light\",primaryColor:\"#6366f1\",tokens:getTokens(dark)}\n }});\n return;\n }\n if (msg.method === \"ui/notifications/initialized\") return;\n\n // Tool calls — proxy via Vite middleware\n if (msg.method === \"tools/call\" && msg.id) {\n var originalId = msg.id;\n try {\n var r = await fetch(\"/__mcp\", {\n method:\"POST\", headers:{\"Content-Type\":\"application/json\"},\n body: JSON.stringify({jsonrpc:\"2.0\",id:msg.id,method:\"tools/call\",params:{name:msg.params.name,arguments:msg.params.arguments||{}}})\n });\n var response = await r.json();\n response.id = originalId;\n post(response);\n // Notify the app that data changed so hooks auto-refresh\n // Only for mutating operations (not list/search/get which are read-only)\n var tn = msg.params.name || \"\";\n if (!response.error && !tn.startsWith(\"list_\") && !tn.startsWith(\"search_\") && !tn.startsWith(\"get_\") && !tn.startsWith(\"query_\")) {\n post({jsonrpc:\"2.0\",method:\"ui/datachanged\",params:{tool:tn,server:\"preview\"}});\n }\n } catch(err) {\n post({jsonrpc:\"2.0\",id:originalId,error:{code:-32000,message:err.message}});\n }\n return;\n }\n\n // Log other messages\n if (msg.method === \"ui/chat\") console.log(\"[chat]\", msg.params?.message);\n else if (msg.method === \"ui/action\") console.log(\"[action]\", msg.params?.action, msg.params);\n else if (msg.method === \"ui/stateChanged\") { console.log(\"[state]\", msg.params?.state); post({jsonrpc:\"2.0\",method:\"ui/stateAcknowledged\",params:{truncated:false}}); }\n else if (msg.method === \"ui/keydown\") { /* ignore */ }\n else if (msg.method) console.log(\"[bridge]\", msg.method, msg);\n });\n\n document.getElementById(\"toggle\").onclick = function() {\n dark = !dark;\n document.body.style.background = dark ? \"#0f172a\" : \"#f1f5f9\";\n post({jsonrpc:\"2.0\",method:\"ui/themeChanged\",params:{mode:dark?\"dark\":\"light\",tokens:getTokens(dark)}});\n };\n\n // Load the iframe AFTER the message listener is attached to avoid\n // a race where the app sends ui/initialize before the bridge is ready.\n iframe.src = \"/\";\n </script>\n</body>\n</html>`;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/vite/plugin.ts"],"names":["resolve"],"mappings":"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","file":"index.js","sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport type { Plugin, ViteDevServer } from \"vite\";\n\nexport interface SynapseVitePluginOptions {\n /** App name. If omitted, reads from ../manifest.json */\n appName?: string;\n /** Path to manifest.json. Default: ../manifest.json (relative to ui/) */\n manifest?: string;\n /**\n * Shell command to start the MCP server. If omitted, derived from manifest.\n * The server runs in stdio mode — stdin/stdout JSON-RPC.\n */\n serverCmd?: string;\n /** Set to false to disable the preview host page at /__preview */\n preview?: boolean;\n}\n\ninterface Manifest {\n name: string;\n version?: string;\n server?: {\n type?: string;\n entry_point?: string;\n mcp_config?: {\n command?: string;\n args?: string[];\n };\n };\n}\n\n/**\n * Synapse Vite plugin — full local dev experience for MCP apps.\n *\n * What it does:\n * - Reads ../manifest.json to get app name and server config\n * - Spawns the MCP server as a child process (stdio mode)\n * - Serves a preview host page at /__preview that iframes your app\n * - Proxies tool calls from the iframe through POST /__mcp to the server\n * - Handles the ext-apps handshake so Synapse hooks work\n * - HMR works inside the iframe — edit .tsx, see changes instantly\n *\n * Usage in vite.config.ts:\n * import { synapseVite } from \"@nimblebrain/synapse/vite\";\n * export default { plugins: [react(), viteSingleFile(), synapseVite()] };\n *\n * Then: cd ui && npm run dev && open http://localhost:5173/__preview\n */\nexport function synapseVite(options: SynapseVitePluginOptions = {}): Plugin {\n const enablePreview = options.preview !== false;\n let manifest: Manifest | null = null;\n let appName = options.appName ?? \"app\";\n let serverProcess: ChildProcess | null = null;\n const pendingRequests = new Map<\n string,\n { resolve: (v: unknown) => void; reject: (e: Error) => void }\n >();\n let serverBuffer = \"\";\n\n function loadManifest(root: string): Manifest | null {\n const manifestPath = options.manifest\n ? resolve(options.manifest)\n : resolve(root, \"..\", \"manifest.json\");\n\n if (!existsSync(manifestPath)) return null;\n try {\n return JSON.parse(readFileSync(manifestPath, \"utf-8\"));\n } catch {\n return null;\n }\n }\n\n function deriveServerCmd(m: Manifest, root: string): string | null {\n if (options.serverCmd) return options.serverCmd;\n const cfg = m.server?.mcp_config;\n if (!cfg?.command) return null;\n\n const serverDir = resolve(root, \"..\");\n let cmd = cfg.command;\n const args = cfg.args ?? [];\n\n // Python projects: use `uv run` if pyproject.toml exists\n if (cmd === \"python\" && existsSync(join(serverDir, \"pyproject.toml\"))) {\n cmd = \"uv run python\";\n }\n\n return `cd ${JSON.stringify(serverDir)} && ${cmd} ${args.join(\" \")}`;\n }\n\n function startServer(cmd: string): void {\n serverProcess = spawn(cmd, {\n shell: true,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n serverProcess.stderr?.on(\"data\", (d: Buffer) => {\n process.stderr.write(` [mcp] ${d}`);\n });\n\n serverProcess.stdout?.on(\"data\", (d: Buffer) => {\n serverBuffer += d.toString();\n // Parse line-delimited JSON-RPC responses\n const lines = serverBuffer.split(\"\\n\");\n serverBuffer = lines.pop() ?? \"\"; // keep incomplete line\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n const msg = JSON.parse(line);\n if (msg.id && pendingRequests.has(msg.id)) {\n const p = pendingRequests.get(msg.id);\n pendingRequests.delete(msg.id);\n p?.resolve(msg);\n }\n } catch {\n // Not JSON — log it\n process.stderr.write(` [mcp] ${line}\\n`);\n }\n }\n });\n\n serverProcess.on(\"exit\", (code) => {\n if (code !== null && code !== 0) {\n console.error(` [mcp] Server exited with code ${code}`);\n }\n serverProcess = null;\n });\n\n // Send initialize\n sendToServer({\n jsonrpc: \"2.0\",\n id: \"init-1\",\n method: \"initialize\",\n params: {\n protocolVersion: \"2024-11-05\",\n capabilities: {},\n clientInfo: { name: \"synapse-preview\", version: \"0.1.0\" },\n },\n });\n }\n\n function sendToServer(msg: Record<string, unknown>): void {\n if (!serverProcess?.stdin?.writable) return;\n serverProcess.stdin.write(`${JSON.stringify(msg)}\\n`);\n }\n\n function callServerTool(name: string, args: Record<string, unknown>): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const id = `preview-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n pendingRequests.set(id, { resolve, reject });\n sendToServer({\n jsonrpc: \"2.0\",\n id,\n method: \"tools/call\",\n params: { name, arguments: args },\n });\n setTimeout(() => {\n if (pendingRequests.has(id)) {\n pendingRequests.delete(id);\n reject(new Error(\"Tool call timed out (10s)\"));\n }\n }, 10000);\n });\n }\n\n return {\n name: \"synapse\",\n\n config() {\n return {\n define: {\n \"import.meta.env.SYNAPSE_APP_NAME\": JSON.stringify(appName),\n },\n server: {\n hmr: {\n protocol: \"ws\",\n host: \"localhost\",\n },\n },\n };\n },\n\n configResolved(config) {\n manifest = loadManifest(config.root);\n if (manifest?.name) {\n appName = options.appName ?? manifest.name;\n }\n },\n\n configureServer(server: ViteDevServer) {\n // Start MCP server\n if (enablePreview && manifest) {\n const cmd = deriveServerCmd(manifest, server.config.root);\n if (cmd) {\n console.log(`\\n [synapse] Starting MCP server: ${cmd}\\n`);\n startServer(cmd);\n }\n }\n\n server.middlewares.use((req, res, next) => {\n // CORS for iframe communication\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"*\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"*\");\n\n // /__preview — bridge host page\n if (req.url === \"/__preview\" || req.url === \"/__preview/\") {\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(previewHostHtml(appName));\n return;\n }\n\n // POST /__mcp — tool call proxy\n if (req.method === \"POST\" && req.url === \"/__mcp\") {\n let body = \"\";\n req.on(\"data\", (chunk: Buffer) => {\n body += chunk.toString();\n });\n req.on(\"end\", async () => {\n try {\n const msg = JSON.parse(body);\n const result = await callServerTool(msg.params.name, msg.params.arguments || {});\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(result));\n } catch (err) {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n jsonrpc: \"2.0\",\n id: JSON.parse(body).id,\n error: { code: -32000, message: (err as Error).message },\n }),\n );\n }\n });\n return;\n }\n\n next();\n });\n },\n\n buildEnd() {\n // Kill server on build end (for production builds)\n if (serverProcess) {\n serverProcess.kill(\"SIGTERM\");\n serverProcess = null;\n }\n },\n };\n}\n\nfunction previewHostHtml(appName: string): string {\n return `<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\" />\n <title>${appName} — Synapse Preview</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; background: #0f172a; color: #e2e8f0; }\n header { padding: 10px 16px; background: #1e293b; border-bottom: 1px solid #334155; display: flex; align-items: center; gap: 10px; font-size: 13px; }\n header .dot { width: 8px; height: 8px; border-radius: 50%; background: #22c55e; }\n header .name { font-weight: 600; }\n header .spacer { flex: 1; }\n header button { background: #334155; border: none; color: #e2e8f0; padding: 3px 8px; border-radius: 4px; cursor: pointer; font-size: 12px; }\n header .url { color: #64748b; font-size: 11px; font-family: monospace; }\n iframe { width: 100%; height: calc(100vh - 41px); border: none; }\n </style>\n</head>\n<body>\n <header>\n <span class=\"dot\"></span>\n <span class=\"name\">${appName}</span>\n <span class=\"spacer\"></span>\n <button id=\"toggle\">Toggle Theme</button>\n <span class=\"url\">Synapse Preview</span>\n </header>\n <iframe id=\"app\"></iframe>\n\n <script>\n var iframe = document.getElementById(\"app\");\n var dark = true;\n\n function getTokens(d) {\n return d ? {\n \"--color-background-primary\":\"#0f172a\",\"--color-text-primary\":\"#e2e8f0\",\n \"--color-background-secondary\":\"#1e293b\",\"--color-text-primary\":\"#e2e8f0\",\n \"--color-text-accent\":\"#6366f1\",\"--nb-color-accent-foreground\":\"#fff\",\n \"--color-text-secondary\":\"#94a3b8\",\"--color-border-primary\":\"#334155\",\n \"--color-ring-primary\":\"#6366f1\",\"--nb-color-danger\":\"#ef4444\",\n \"--border-radius-sm\":\"0.5rem\",\"--font-sans\":\"-apple-system,BlinkMacSystemFont,sans-serif\"\n } : {\n \"--color-background-primary\":\"#ffffff\",\"--color-text-primary\":\"#0f172a\",\n \"--color-background-secondary\":\"#f8fafc\",\"--color-text-primary\":\"#0f172a\",\n \"--color-text-accent\":\"#6366f1\",\"--nb-color-accent-foreground\":\"#fff\",\n \"--color-text-secondary\":\"#64748b\",\"--color-border-primary\":\"#e2e8f0\",\n \"--color-ring-primary\":\"#6366f1\",\"--nb-color-danger\":\"#ef4444\",\n \"--border-radius-sm\":\"0.5rem\",\"--font-sans\":\"-apple-system,BlinkMacSystemFont,sans-serif\"\n };\n }\n\n function post(msg) { iframe.contentWindow.postMessage(msg, \"*\"); }\n\n window.addEventListener(\"message\", async function(e) {\n if (e.source !== iframe.contentWindow) return;\n var msg = e.data;\n if (!msg || typeof msg !== \"object\") return;\n\n // ext-apps handshake\n if (msg.method === \"ui/initialize\" && msg.id) {\n post({ jsonrpc:\"2.0\", id:msg.id, result: {\n protocolVersion:\"2026-01-26\",\n serverInfo:{name:\"nimblebrain\",version:\"preview\"},\n capabilities:{openLinks:{},serverTools:{}},\n hostContext:{theme:dark?\"dark\":\"light\",primaryColor:\"#6366f1\",tokens:getTokens(dark)}\n }});\n return;\n }\n if (msg.method === \"ui/notifications/initialized\") return;\n\n // Tool calls — proxy via Vite middleware\n if (msg.method === \"tools/call\" && msg.id) {\n var originalId = msg.id;\n try {\n var r = await fetch(\"/__mcp\", {\n method:\"POST\", headers:{\"Content-Type\":\"application/json\"},\n body: JSON.stringify({jsonrpc:\"2.0\",id:msg.id,method:\"tools/call\",params:{name:msg.params.name,arguments:msg.params.arguments||{}}})\n });\n var response = await r.json();\n response.id = originalId;\n post(response);\n // Notify the app that data changed so hooks auto-refresh\n // Only for mutating operations (not list/search/get which are read-only)\n var tn = msg.params.name || \"\";\n if (!response.error && !tn.startsWith(\"list_\") && !tn.startsWith(\"search_\") && !tn.startsWith(\"get_\") && !tn.startsWith(\"query_\")) {\n post({jsonrpc:\"2.0\",method:\"synapse/data-changed\",params:{tool:tn,server:\"preview\"}});\n }\n } catch(err) {\n post({jsonrpc:\"2.0\",id:originalId,error:{code:-32000,message:err.message}});\n }\n return;\n }\n\n // Log other messages\n if (msg.method === \"synapse/chat\") console.log(\"[chat]\", msg.params?.message);\n else if (msg.method === \"synapse/action\") console.log(\"[action]\", msg.params?.action, msg.params);\n else if (msg.method === \"ui/update-model-context\") { console.log(\"[model-context]\", msg.params?.structuredContent); if (msg.id) post({jsonrpc:\"2.0\",id:msg.id,result:{}}); }\n else if (msg.method === \"synapse/keydown\") { /* ignore */ }\n else if (msg.method) console.log(\"[bridge]\", msg.method, msg);\n });\n\n document.getElementById(\"toggle\").onclick = function() {\n dark = !dark;\n document.body.style.background = dark ? \"#0f172a\" : \"#f1f5f9\";\n post({jsonrpc:\"2.0\",method:\"synapse/theme-changed\",params:{mode:dark?\"dark\":\"light\",tokens:getTokens(dark)}});\n };\n\n // Load the iframe AFTER the message listener is attached to avoid\n // a race where the app sends ui/initialize before the bridge is ready.\n iframe.src = \"/\";\n </script>\n</body>\n</html>`;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/detection.ts","../src/keyboard.ts","../src/result-parser.ts","../src/transport.ts","../src/core.ts"],"names":[],"mappings":"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","file":"chunk-7KEYXJWD.js","sourcesContent":["import type { HostInfo, SynapseTheme } from \"./types\";\n\nconst DEFAULT_THEME: SynapseTheme = {\n mode: \"light\",\n primaryColor: \"#6366f1\",\n tokens: {},\n};\n\n/**\n * Detect the host environment from the ext-apps `ui/initialize` response.\n *\n * Handles missing or malformed fields gracefully — never throws.\n */\nexport function detectHost(initResponse: unknown): HostInfo {\n const resp = initResponse as Record<string, unknown> | null | undefined;\n\n const serverInfo = safeObj(resp?.serverInfo);\n const serverName = typeof serverInfo?.name === \"string\" ? serverInfo.name : \"unknown\";\n\n const protocolVersion =\n typeof resp?.protocolVersion === \"string\" ? resp.protocolVersion : \"unknown\";\n\n const hostContext = safeObj(resp?.hostContext);\n const theme = extractTheme(hostContext?.theme);\n\n return {\n isNimbleBrain: serverName === \"nimblebrain\",\n serverName,\n protocolVersion,\n theme,\n };\n}\n\nfunction extractTheme(raw: unknown): SynapseTheme {\n const obj = safeObj(raw);\n if (!obj) return { ...DEFAULT_THEME };\n\n const mode = obj.mode === \"light\" || obj.mode === \"dark\" ? obj.mode : DEFAULT_THEME.mode;\n\n const primaryColor =\n typeof obj.primaryColor === \"string\" ? obj.primaryColor : DEFAULT_THEME.primaryColor;\n\n const tokens =\n obj.tokens !== null && typeof obj.tokens === \"object\" && !Array.isArray(obj.tokens)\n ? (obj.tokens as Record<string, string>)\n : {};\n\n return { mode, primaryColor, tokens };\n}\n\nfunction safeObj(value: unknown): Record<string, unknown> | undefined {\n if (value !== null && typeof value === \"object\" && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n return undefined;\n}\n","import type { SynapseTransport } from \"./transport.js\";\nimport type { KeyForwardConfig } from \"./types.js\";\n\n/**\n * Forward keyboard shortcuts from the iframe document to the host.\n *\n * By default, forwards all Ctrl/Cmd+key combos and Escape.\n * Apps can customize via `forwardKeys` config.\n */\nexport class KeyboardForwarder {\n private listener: (event: KeyboardEvent) => void;\n private destroyed = false;\n\n constructor(transport: SynapseTransport, customKeys?: KeyForwardConfig[]) {\n const config = customKeys ?? null; // null = default behavior\n\n this.listener = (event: KeyboardEvent) => {\n if (this.destroyed) return;\n if (this.shouldForward(event, config)) {\n event.preventDefault();\n transport.send(\"ui/keydown\", {\n key: event.key,\n ctrlKey: event.ctrlKey,\n metaKey: event.metaKey,\n shiftKey: event.shiftKey,\n altKey: event.altKey,\n });\n }\n };\n\n document.addEventListener(\"keydown\", this.listener);\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n document.removeEventListener(\"keydown\", this.listener);\n }\n\n private shouldForward(event: KeyboardEvent, config: KeyForwardConfig[] | null): boolean {\n // Empty array = forwarding disabled\n if (config && config.length === 0) return false;\n\n // Custom config: match exactly\n if (config) {\n return config.some(\n (k) =>\n event.key.toLowerCase() === k.key.toLowerCase() &&\n (k.ctrl === undefined || event.ctrlKey === k.ctrl) &&\n (k.meta === undefined || event.metaKey === k.meta) &&\n (k.shift === undefined || event.shiftKey === k.shift) &&\n (k.alt === undefined || event.altKey === k.alt),\n );\n }\n\n // Default: forward all Ctrl/Cmd combos + Escape,\n // EXCEPT clipboard shortcuts (c, v, x, a) which the browser must handle natively.\n if (event.key === \"Escape\") return true;\n if (event.ctrlKey || event.metaKey) {\n const key = event.key.toLowerCase();\n if (key === \"c\" || key === \"v\" || key === \"x\" || key === \"a\") return false;\n return true;\n }\n return false;\n }\n}\n","import type { ToolCallResult } from \"./types.js\";\n\n/**\n * Normalize a raw tool call response into a consistent `ToolCallResult`.\n *\n * Handles three shapes:\n * 1. MCP `CallToolResult` — has a `content` array with typed blocks.\n * 2. Raw JSON object (NimbleBrain bridge) — used as-is.\n * 3. Null / undefined — returns `{ data: null, isError: false }`.\n */\nexport function parseToolResult(raw: unknown): ToolCallResult {\n if (raw == null) {\n return { data: null, isError: false };\n }\n\n if (isCallToolResult(raw)) {\n return parseCallToolResult(raw);\n }\n\n // Raw JSON object — pass through.\n return { data: raw, isError: false };\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\ninterface McpTextBlock {\n type: \"text\";\n text: string;\n}\n\ninterface McpCallToolResult {\n content: unknown[];\n isError?: boolean;\n}\n\nfunction isCallToolResult(value: unknown): value is McpCallToolResult {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n return false;\n }\n return Array.isArray((value as Record<string, unknown>).content);\n}\n\nfunction isTextBlock(block: unknown): block is McpTextBlock {\n if (block === null || typeof block !== \"object\" || Array.isArray(block)) {\n return false;\n }\n const obj = block as Record<string, unknown>;\n return obj.type === \"text\" && typeof obj.text === \"string\";\n}\n\nfunction parseCallToolResult(result: McpCallToolResult): ToolCallResult {\n const isError = result.isError === true;\n const content = result.content;\n\n if (content.length === 0) {\n return { data: null, isError };\n }\n\n const firstText = content.find(isTextBlock);\n\n if (!firstText) {\n // No text blocks — return the full content array so callers can inspect it.\n return { data: content, isError };\n }\n\n // Try to parse JSON from the text block.\n try {\n return { data: JSON.parse(firstText.text), isError };\n } catch {\n // Invalid JSON — return the raw string.\n return { data: firstText.text, isError };\n }\n}\n","import type {\n JsonRpcMessage,\n JsonRpcNotification,\n JsonRpcRequest,\n JsonRpcResponse,\n} from \"./types.js\";\n\ntype PendingEntry = {\n resolve: (value: unknown) => void;\n reject: (reason: unknown) => void;\n};\n\ntype MessageHandler = (params: Record<string, unknown> | undefined) => void;\n\nexport class SynapseTransport {\n private counter = 0;\n private destroyed = false;\n private pending = new Map<string, PendingEntry>();\n private handlers = new Map<string, Set<MessageHandler>>();\n private listener: (event: MessageEvent) => void;\n\n constructor() {\n this.listener = (event: MessageEvent) => this.handleMessage(event);\n window.addEventListener(\"message\", this.listener);\n }\n\n send(method: string, params?: Record<string, unknown>): void {\n if (this.destroyed) return;\n\n const msg: JsonRpcNotification = {\n jsonrpc: \"2.0\",\n method,\n ...(params !== undefined && { params }),\n };\n window.parent.postMessage(msg, \"*\");\n }\n\n request(method: string, params?: Record<string, unknown>): Promise<unknown> {\n if (this.destroyed) {\n return Promise.reject(new Error(\"Transport destroyed\"));\n }\n\n const id = `syn-${++this.counter}`;\n const msg: JsonRpcRequest = {\n jsonrpc: \"2.0\",\n method,\n id,\n ...(params !== undefined && { params }),\n };\n\n return new Promise<unknown>((resolve, reject) => {\n this.pending.set(id, { resolve, reject });\n window.parent.postMessage(msg, \"*\");\n });\n }\n\n onMessage(method: string, callback: MessageHandler): () => void {\n if (!this.handlers.has(method)) {\n this.handlers.set(method, new Set());\n }\n this.handlers.get(method)?.add(callback);\n\n return () => {\n const set = this.handlers.get(method);\n if (set) {\n set.delete(callback);\n if (set.size === 0) {\n this.handlers.delete(method);\n }\n }\n };\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n\n window.removeEventListener(\"message\", this.listener);\n\n const error = new Error(\"Transport destroyed\");\n for (const entry of this.pending.values()) {\n entry.reject(error);\n }\n this.pending.clear();\n this.handlers.clear();\n }\n\n private handleMessage(event: MessageEvent): void {\n if (this.destroyed) return;\n\n const data = event.data as JsonRpcMessage;\n if (!data || data.jsonrpc !== \"2.0\") return;\n\n // Response to a pending request\n if (\"id\" in data && data.id && !(\"method\" in data)) {\n const response = data as JsonRpcResponse;\n const entry = this.pending.get(response.id);\n if (!entry) return;\n this.pending.delete(response.id);\n\n if (response.error) {\n const err = new Error(response.error.message);\n (err as any).code = response.error.code;\n (err as any).data = response.error.data;\n entry.reject(err);\n } else {\n entry.resolve(response.result);\n }\n return;\n }\n\n // Incoming notification\n if (\"method\" in data && !(\"id\" in data && data.id)) {\n const notification = data as JsonRpcNotification;\n const set = this.handlers.get(notification.method);\n if (set) {\n for (const handler of set) {\n handler(notification.params);\n }\n }\n }\n }\n}\n","import { detectHost } from \"./detection.js\";\nimport { KeyboardForwarder } from \"./keyboard.js\";\nimport { parseToolResult } from \"./result-parser.js\";\nimport { SynapseTransport } from \"./transport.js\";\nimport type {\n AgentAction,\n DataChangedEvent,\n HostInfo,\n Synapse,\n SynapseOptions,\n SynapseTheme,\n ToolCallResult,\n} from \"./types.js\";\n\n/**\n * Create a Synapse instance.\n *\n * Wraps the ext-apps protocol handshake via `SynapseTransport` and provides\n * a typed, framework-agnostic API for calling tools, reacting to data changes,\n * dispatching actions, and pushing LLM-visible state.\n *\n * In non-NimbleBrain hosts, NB-specific methods degrade to no-ops.\n */\nexport function createSynapse(options: SynapseOptions): Synapse {\n const { name, version, internal = false, forwardKeys } = options;\n\n const transport = new SynapseTransport();\n let hostInfo: HostInfo | null = null;\n let currentTheme: SynapseTheme = {\n mode: \"light\",\n primaryColor: \"#6366f1\",\n tokens: {},\n };\n let destroyed = false;\n\n // --- Debounce for setVisibleState ---\n let stateTimer: ReturnType<typeof setTimeout> | null = null;\n\n // --- Keyboard forwarding ---\n let keyboard: KeyboardForwarder | null = null;\n\n // --- ext-apps handshake ---\n // We send ui/initialize as a JSON-RPC request and wait for the response.\n const ready = transport\n .request(\"ui/initialize\", {\n protocolVersion: \"2026-01-26\",\n clientInfo: { name, version },\n capabilities: {},\n })\n .then((result) => {\n hostInfo = detectHost(result);\n currentTheme = hostInfo.theme;\n\n // Send initialized notification per ext-apps spec\n transport.send(\"ui/notifications/initialized\", {});\n\n // Set up keyboard forwarding after we know the host\n keyboard = new KeyboardForwarder(transport, forwardKeys);\n });\n\n // Listen for theme changes from the host\n const unsubTheme = transport.onMessage(\"ui/notifications/host-context-changed\", (params) => {\n if (!params) return;\n const mode = params.theme === \"dark\" ? \"dark\" : \"light\";\n const tokens =\n params.tokens && typeof params.tokens === \"object\"\n ? (params.tokens as Record<string, string>)\n : currentTheme.tokens;\n currentTheme = { mode, primaryColor: currentTheme.primaryColor, tokens };\n for (const cb of themeCallbacks) cb(currentTheme);\n });\n\n // Also listen for NB-specific theme change message\n const unsubNbTheme = transport.onMessage(\"ui/themeChanged\", (params) => {\n if (!params) return;\n const mode =\n params.mode === \"dark\" || params.mode === \"light\" ? params.mode : currentTheme.mode;\n const tokens =\n params.tokens && typeof params.tokens === \"object\"\n ? (params.tokens as Record<string, string>)\n : currentTheme.tokens;\n currentTheme = { mode, primaryColor: currentTheme.primaryColor, tokens };\n for (const cb of themeCallbacks) cb(currentTheme);\n });\n\n const themeCallbacks = new Set<(theme: SynapseTheme) => void>();\n const dataCallbacks = new Set<(event: DataChangedEvent) => void>();\n const actionCallbacks = new Set<(action: AgentAction) => void>();\n\n // Listen for data change events\n const unsubData = transport.onMessage(\"ui/datachanged\", (params) => {\n if (!params) return;\n const event: DataChangedEvent = {\n source: \"agent\",\n server: (params.server as string) ?? \"\",\n tool: (params.tool as string) ?? \"\",\n };\n for (const cb of dataCallbacks) cb(event);\n });\n\n // Listen for agent actions (typed, declarative commands from the server)\n const unsubAction = transport.onMessage(\"ui/action\", (params) => {\n if (!params || typeof params.type !== \"string\") return;\n const action: AgentAction = {\n type: params.type as string,\n payload: (params.payload as Record<string, unknown>) ?? {},\n requiresConfirmation: params.requiresConfirmation === true,\n label: typeof params.label === \"string\" ? params.label : undefined,\n };\n for (const cb of actionCallbacks) cb(action);\n });\n\n const isNB = () => hostInfo?.isNimbleBrain === true;\n\n const synapse: Synapse = {\n get ready() {\n return ready;\n },\n\n get isNimbleBrainHost() {\n return isNB();\n },\n\n get destroyed() {\n return destroyed;\n },\n\n async callTool<TInput = Record<string, unknown>, TOutput = unknown>(\n toolName: string,\n args?: TInput,\n ): Promise<ToolCallResult<TOutput>> {\n const params: Record<string, unknown> = {\n name: toolName,\n arguments: args ?? {},\n };\n // Internal apps can cross-call\n if (internal) {\n params.server = name;\n }\n const raw = await transport.request(\"tools/call\", params);\n return parseToolResult(raw) as ToolCallResult<TOutput>;\n },\n\n onDataChanged(callback: (event: DataChangedEvent) => void): () => void {\n dataCallbacks.add(callback);\n return () => {\n dataCallbacks.delete(callback);\n };\n },\n\n onAction(callback: (action: AgentAction) => void): () => void {\n actionCallbacks.add(callback);\n return () => {\n actionCallbacks.delete(callback);\n };\n },\n\n getTheme(): SynapseTheme {\n return { ...currentTheme };\n },\n\n onThemeChanged(callback: (theme: SynapseTheme) => void): () => void {\n themeCallbacks.add(callback);\n return () => {\n themeCallbacks.delete(callback);\n };\n },\n\n action(action: string, params?: Record<string, unknown>): void {\n if (!isNB()) return;\n transport.send(\"ui/action\", { action, ...params });\n },\n\n chat(message: string, context?: { action?: string; entity?: string }): void {\n const textBlock: Record<string, unknown> = { type: \"text\", text: message };\n if (isNB() && context) {\n textBlock._meta = { context };\n }\n transport.send(\"ui/message\", {\n role: \"user\",\n content: [textBlock],\n });\n },\n\n setVisibleState(state: Record<string, unknown>, summary?: string): void {\n // Debounce: 250ms\n if (stateTimer) clearTimeout(stateTimer);\n stateTimer = setTimeout(() => {\n transport.send(\"ui/update-model-context\", {\n structuredContent: state,\n ...(summary !== undefined && {\n content: [{ type: \"text\", text: summary }],\n }),\n });\n stateTimer = null;\n }, 250);\n },\n\n downloadFile(filename: string, content: string | Blob, mimeType?: string): void {\n if (!isNB()) return;\n const data = typeof content === \"string\" ? content : \"[Blob content not serializable]\";\n transport.send(\"ui/download-file\", {\n data,\n filename,\n mimeType: mimeType ?? \"application/octet-stream\",\n });\n },\n\n openLink(url: string): void {\n transport.send(\"ui/open-link\", { url });\n if (!isNB()) {\n window.open(url, \"_blank\", \"noopener\");\n }\n },\n\n _onMessage(\n method: string,\n callback: (params: Record<string, unknown> | undefined) => void,\n ): () => void {\n return transport.onMessage(method, callback);\n },\n\n _request(method: string, params?: Record<string, unknown>): Promise<unknown> {\n return transport.request(method, params);\n },\n\n destroy(): void {\n if (destroyed) return;\n destroyed = true;\n\n if (stateTimer) clearTimeout(stateTimer);\n keyboard?.destroy();\n unsubTheme();\n unsubNbTheme();\n unsubData();\n unsubAction();\n themeCallbacks.clear();\n dataCallbacks.clear();\n actionCallbacks.clear();\n transport.destroy();\n },\n };\n\n return synapse;\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/detection.ts","../src/keyboard.ts","../src/result-parser.ts","../src/transport.ts","../src/core.ts"],"names":[],"mappings":"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","file":"chunk-Y4ZDNAYQ.cjs","sourcesContent":["import type { HostInfo, SynapseTheme } from \"./types\";\n\nconst DEFAULT_THEME: SynapseTheme = {\n mode: \"light\",\n primaryColor: \"#6366f1\",\n tokens: {},\n};\n\n/**\n * Detect the host environment from the ext-apps `ui/initialize` response.\n *\n * Handles missing or malformed fields gracefully — never throws.\n */\nexport function detectHost(initResponse: unknown): HostInfo {\n const resp = initResponse as Record<string, unknown> | null | undefined;\n\n const serverInfo = safeObj(resp?.serverInfo);\n const serverName = typeof serverInfo?.name === \"string\" ? serverInfo.name : \"unknown\";\n\n const protocolVersion =\n typeof resp?.protocolVersion === \"string\" ? resp.protocolVersion : \"unknown\";\n\n const hostContext = safeObj(resp?.hostContext);\n const theme = extractTheme(hostContext?.theme);\n\n return {\n isNimbleBrain: serverName === \"nimblebrain\",\n serverName,\n protocolVersion,\n theme,\n };\n}\n\nfunction extractTheme(raw: unknown): SynapseTheme {\n const obj = safeObj(raw);\n if (!obj) return { ...DEFAULT_THEME };\n\n const mode = obj.mode === \"light\" || obj.mode === \"dark\" ? obj.mode : DEFAULT_THEME.mode;\n\n const primaryColor =\n typeof obj.primaryColor === \"string\" ? obj.primaryColor : DEFAULT_THEME.primaryColor;\n\n const tokens =\n obj.tokens !== null && typeof obj.tokens === \"object\" && !Array.isArray(obj.tokens)\n ? (obj.tokens as Record<string, string>)\n : {};\n\n return { mode, primaryColor, tokens };\n}\n\nfunction safeObj(value: unknown): Record<string, unknown> | undefined {\n if (value !== null && typeof value === \"object\" && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n return undefined;\n}\n","import type { SynapseTransport } from \"./transport.js\";\nimport type { KeyForwardConfig } from \"./types.js\";\n\n/**\n * Forward keyboard shortcuts from the iframe document to the host.\n *\n * By default, forwards all Ctrl/Cmd+key combos and Escape.\n * Apps can customize via `forwardKeys` config.\n */\nexport class KeyboardForwarder {\n private listener: (event: KeyboardEvent) => void;\n private destroyed = false;\n\n constructor(transport: SynapseTransport, customKeys?: KeyForwardConfig[]) {\n const config = customKeys ?? null; // null = default behavior\n\n this.listener = (event: KeyboardEvent) => {\n if (this.destroyed) return;\n if (this.shouldForward(event, config)) {\n event.preventDefault();\n transport.send(\"ui/keydown\", {\n key: event.key,\n ctrlKey: event.ctrlKey,\n metaKey: event.metaKey,\n shiftKey: event.shiftKey,\n altKey: event.altKey,\n });\n }\n };\n\n document.addEventListener(\"keydown\", this.listener);\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n document.removeEventListener(\"keydown\", this.listener);\n }\n\n private shouldForward(event: KeyboardEvent, config: KeyForwardConfig[] | null): boolean {\n // Empty array = forwarding disabled\n if (config && config.length === 0) return false;\n\n // Custom config: match exactly\n if (config) {\n return config.some(\n (k) =>\n event.key.toLowerCase() === k.key.toLowerCase() &&\n (k.ctrl === undefined || event.ctrlKey === k.ctrl) &&\n (k.meta === undefined || event.metaKey === k.meta) &&\n (k.shift === undefined || event.shiftKey === k.shift) &&\n (k.alt === undefined || event.altKey === k.alt),\n );\n }\n\n // Default: forward all Ctrl/Cmd combos + Escape,\n // EXCEPT clipboard shortcuts (c, v, x, a) which the browser must handle natively.\n if (event.key === \"Escape\") return true;\n if (event.ctrlKey || event.metaKey) {\n const key = event.key.toLowerCase();\n if (key === \"c\" || key === \"v\" || key === \"x\" || key === \"a\") return false;\n return true;\n }\n return false;\n }\n}\n","import type { ToolCallResult } from \"./types.js\";\n\n/**\n * Normalize a raw tool call response into a consistent `ToolCallResult`.\n *\n * Handles three shapes:\n * 1. MCP `CallToolResult` — has a `content` array with typed blocks.\n * 2. Raw JSON object (NimbleBrain bridge) — used as-is.\n * 3. Null / undefined — returns `{ data: null, isError: false }`.\n */\nexport function parseToolResult(raw: unknown): ToolCallResult {\n if (raw == null) {\n return { data: null, isError: false };\n }\n\n if (isCallToolResult(raw)) {\n return parseCallToolResult(raw);\n }\n\n // Raw JSON object — pass through.\n return { data: raw, isError: false };\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\ninterface McpTextBlock {\n type: \"text\";\n text: string;\n}\n\ninterface McpCallToolResult {\n content: unknown[];\n isError?: boolean;\n}\n\nfunction isCallToolResult(value: unknown): value is McpCallToolResult {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n return false;\n }\n return Array.isArray((value as Record<string, unknown>).content);\n}\n\nfunction isTextBlock(block: unknown): block is McpTextBlock {\n if (block === null || typeof block !== \"object\" || Array.isArray(block)) {\n return false;\n }\n const obj = block as Record<string, unknown>;\n return obj.type === \"text\" && typeof obj.text === \"string\";\n}\n\nfunction parseCallToolResult(result: McpCallToolResult): ToolCallResult {\n const isError = result.isError === true;\n const content = result.content;\n\n if (content.length === 0) {\n return { data: null, isError };\n }\n\n const firstText = content.find(isTextBlock);\n\n if (!firstText) {\n // No text blocks — return the full content array so callers can inspect it.\n return { data: content, isError };\n }\n\n // Try to parse JSON from the text block.\n try {\n return { data: JSON.parse(firstText.text), isError };\n } catch {\n // Invalid JSON — return the raw string.\n return { data: firstText.text, isError };\n }\n}\n","import type {\n JsonRpcMessage,\n JsonRpcNotification,\n JsonRpcRequest,\n JsonRpcResponse,\n} from \"./types.js\";\n\ntype PendingEntry = {\n resolve: (value: unknown) => void;\n reject: (reason: unknown) => void;\n};\n\ntype MessageHandler = (params: Record<string, unknown> | undefined) => void;\n\nexport class SynapseTransport {\n private counter = 0;\n private destroyed = false;\n private pending = new Map<string, PendingEntry>();\n private handlers = new Map<string, Set<MessageHandler>>();\n private listener: (event: MessageEvent) => void;\n\n constructor() {\n this.listener = (event: MessageEvent) => this.handleMessage(event);\n window.addEventListener(\"message\", this.listener);\n }\n\n send(method: string, params?: Record<string, unknown>): void {\n if (this.destroyed) return;\n\n const msg: JsonRpcNotification = {\n jsonrpc: \"2.0\",\n method,\n ...(params !== undefined && { params }),\n };\n window.parent.postMessage(msg, \"*\");\n }\n\n request(method: string, params?: Record<string, unknown>): Promise<unknown> {\n if (this.destroyed) {\n return Promise.reject(new Error(\"Transport destroyed\"));\n }\n\n const id = `syn-${++this.counter}`;\n const msg: JsonRpcRequest = {\n jsonrpc: \"2.0\",\n method,\n id,\n ...(params !== undefined && { params }),\n };\n\n return new Promise<unknown>((resolve, reject) => {\n this.pending.set(id, { resolve, reject });\n window.parent.postMessage(msg, \"*\");\n });\n }\n\n onMessage(method: string, callback: MessageHandler): () => void {\n if (!this.handlers.has(method)) {\n this.handlers.set(method, new Set());\n }\n this.handlers.get(method)?.add(callback);\n\n return () => {\n const set = this.handlers.get(method);\n if (set) {\n set.delete(callback);\n if (set.size === 0) {\n this.handlers.delete(method);\n }\n }\n };\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n\n window.removeEventListener(\"message\", this.listener);\n\n const error = new Error(\"Transport destroyed\");\n for (const entry of this.pending.values()) {\n entry.reject(error);\n }\n this.pending.clear();\n this.handlers.clear();\n }\n\n private handleMessage(event: MessageEvent): void {\n if (this.destroyed) return;\n\n const data = event.data as JsonRpcMessage;\n if (!data || data.jsonrpc !== \"2.0\") return;\n\n // Response to a pending request\n if (\"id\" in data && data.id && !(\"method\" in data)) {\n const response = data as JsonRpcResponse;\n const entry = this.pending.get(response.id);\n if (!entry) return;\n this.pending.delete(response.id);\n\n if (response.error) {\n const err = new Error(response.error.message);\n (err as any).code = response.error.code;\n (err as any).data = response.error.data;\n entry.reject(err);\n } else {\n entry.resolve(response.result);\n }\n return;\n }\n\n // Incoming notification\n if (\"method\" in data && !(\"id\" in data && data.id)) {\n const notification = data as JsonRpcNotification;\n const set = this.handlers.get(notification.method);\n if (set) {\n for (const handler of set) {\n handler(notification.params);\n }\n }\n }\n }\n}\n","import { detectHost } from \"./detection.js\";\nimport { KeyboardForwarder } from \"./keyboard.js\";\nimport { parseToolResult } from \"./result-parser.js\";\nimport { SynapseTransport } from \"./transport.js\";\nimport type {\n AgentAction,\n DataChangedEvent,\n HostInfo,\n Synapse,\n SynapseOptions,\n SynapseTheme,\n ToolCallResult,\n} from \"./types.js\";\n\n/**\n * Create a Synapse instance.\n *\n * Wraps the ext-apps protocol handshake via `SynapseTransport` and provides\n * a typed, framework-agnostic API for calling tools, reacting to data changes,\n * dispatching actions, and pushing LLM-visible state.\n *\n * In non-NimbleBrain hosts, NB-specific methods degrade to no-ops.\n */\nexport function createSynapse(options: SynapseOptions): Synapse {\n const { name, version, internal = false, forwardKeys } = options;\n\n const transport = new SynapseTransport();\n let hostInfo: HostInfo | null = null;\n let currentTheme: SynapseTheme = {\n mode: \"light\",\n primaryColor: \"#6366f1\",\n tokens: {},\n };\n let destroyed = false;\n\n // --- Debounce for setVisibleState ---\n let stateTimer: ReturnType<typeof setTimeout> | null = null;\n\n // --- Keyboard forwarding ---\n let keyboard: KeyboardForwarder | null = null;\n\n // --- ext-apps handshake ---\n // We send ui/initialize as a JSON-RPC request and wait for the response.\n const ready = transport\n .request(\"ui/initialize\", {\n protocolVersion: \"2026-01-26\",\n clientInfo: { name, version },\n capabilities: {},\n })\n .then((result) => {\n hostInfo = detectHost(result);\n currentTheme = hostInfo.theme;\n\n // Send initialized notification per ext-apps spec\n transport.send(\"ui/notifications/initialized\", {});\n\n // Set up keyboard forwarding after we know the host\n keyboard = new KeyboardForwarder(transport, forwardKeys);\n });\n\n // Listen for theme changes from the host\n const unsubTheme = transport.onMessage(\"ui/notifications/host-context-changed\", (params) => {\n if (!params) return;\n const mode = params.theme === \"dark\" ? \"dark\" : \"light\";\n const tokens =\n params.tokens && typeof params.tokens === \"object\"\n ? (params.tokens as Record<string, string>)\n : currentTheme.tokens;\n currentTheme = { mode, primaryColor: currentTheme.primaryColor, tokens };\n for (const cb of themeCallbacks) cb(currentTheme);\n });\n\n // Also listen for NB-specific theme change message\n const unsubNbTheme = transport.onMessage(\"ui/themeChanged\", (params) => {\n if (!params) return;\n const mode =\n params.mode === \"dark\" || params.mode === \"light\" ? params.mode : currentTheme.mode;\n const tokens =\n params.tokens && typeof params.tokens === \"object\"\n ? (params.tokens as Record<string, string>)\n : currentTheme.tokens;\n currentTheme = { mode, primaryColor: currentTheme.primaryColor, tokens };\n for (const cb of themeCallbacks) cb(currentTheme);\n });\n\n const themeCallbacks = new Set<(theme: SynapseTheme) => void>();\n const dataCallbacks = new Set<(event: DataChangedEvent) => void>();\n const actionCallbacks = new Set<(action: AgentAction) => void>();\n\n // Listen for data change events\n const unsubData = transport.onMessage(\"ui/datachanged\", (params) => {\n if (!params) return;\n const event: DataChangedEvent = {\n source: \"agent\",\n server: (params.server as string) ?? \"\",\n tool: (params.tool as string) ?? \"\",\n };\n for (const cb of dataCallbacks) cb(event);\n });\n\n // Listen for agent actions (typed, declarative commands from the server)\n const unsubAction = transport.onMessage(\"ui/action\", (params) => {\n if (!params || typeof params.type !== \"string\") return;\n const action: AgentAction = {\n type: params.type as string,\n payload: (params.payload as Record<string, unknown>) ?? {},\n requiresConfirmation: params.requiresConfirmation === true,\n label: typeof params.label === \"string\" ? params.label : undefined,\n };\n for (const cb of actionCallbacks) cb(action);\n });\n\n const isNB = () => hostInfo?.isNimbleBrain === true;\n\n const synapse: Synapse = {\n get ready() {\n return ready;\n },\n\n get isNimbleBrainHost() {\n return isNB();\n },\n\n get destroyed() {\n return destroyed;\n },\n\n async callTool<TInput = Record<string, unknown>, TOutput = unknown>(\n toolName: string,\n args?: TInput,\n ): Promise<ToolCallResult<TOutput>> {\n const params: Record<string, unknown> = {\n name: toolName,\n arguments: args ?? {},\n };\n // Internal apps can cross-call\n if (internal) {\n params.server = name;\n }\n const raw = await transport.request(\"tools/call\", params);\n return parseToolResult(raw) as ToolCallResult<TOutput>;\n },\n\n onDataChanged(callback: (event: DataChangedEvent) => void): () => void {\n dataCallbacks.add(callback);\n return () => {\n dataCallbacks.delete(callback);\n };\n },\n\n onAction(callback: (action: AgentAction) => void): () => void {\n actionCallbacks.add(callback);\n return () => {\n actionCallbacks.delete(callback);\n };\n },\n\n getTheme(): SynapseTheme {\n return { ...currentTheme };\n },\n\n onThemeChanged(callback: (theme: SynapseTheme) => void): () => void {\n themeCallbacks.add(callback);\n return () => {\n themeCallbacks.delete(callback);\n };\n },\n\n action(action: string, params?: Record<string, unknown>): void {\n if (!isNB()) return;\n transport.send(\"ui/action\", { action, ...params });\n },\n\n chat(message: string, context?: { action?: string; entity?: string }): void {\n const textBlock: Record<string, unknown> = { type: \"text\", text: message };\n if (isNB() && context) {\n textBlock._meta = { context };\n }\n transport.send(\"ui/message\", {\n role: \"user\",\n content: [textBlock],\n });\n },\n\n setVisibleState(state: Record<string, unknown>, summary?: string): void {\n // Debounce: 250ms\n if (stateTimer) clearTimeout(stateTimer);\n stateTimer = setTimeout(() => {\n transport.send(\"ui/update-model-context\", {\n structuredContent: state,\n ...(summary !== undefined && {\n content: [{ type: \"text\", text: summary }],\n }),\n });\n stateTimer = null;\n }, 250);\n },\n\n downloadFile(filename: string, content: string | Blob, mimeType?: string): void {\n if (!isNB()) return;\n const data = typeof content === \"string\" ? content : \"[Blob content not serializable]\";\n transport.send(\"ui/download-file\", {\n data,\n filename,\n mimeType: mimeType ?? \"application/octet-stream\",\n });\n },\n\n openLink(url: string): void {\n transport.send(\"ui/open-link\", { url });\n if (!isNB()) {\n window.open(url, \"_blank\", \"noopener\");\n }\n },\n\n _onMessage(\n method: string,\n callback: (params: Record<string, unknown> | undefined) => void,\n ): () => void {\n return transport.onMessage(method, callback);\n },\n\n _request(method: string, params?: Record<string, unknown>): Promise<unknown> {\n return transport.request(method, params);\n },\n\n destroy(): void {\n if (destroyed) return;\n destroyed = true;\n\n if (stateTimer) clearTimeout(stateTimer);\n keyboard?.destroy();\n unsubTheme();\n unsubNbTheme();\n unsubData();\n unsubAction();\n themeCallbacks.clear();\n dataCallbacks.clear();\n actionCallbacks.clear();\n transport.destroy();\n },\n };\n\n return synapse;\n}\n"]}
|