@nimblebrain/synapse 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +248 -0
- package/dist/chunk-AW3YIXLE.cjs +248 -0
- package/dist/chunk-AW3YIXLE.cjs.map +1 -0
- package/dist/chunk-JZC3VC2C.js +349 -0
- package/dist/chunk-JZC3VC2C.js.map +1 -0
- package/dist/chunk-M4I222LB.js +243 -0
- package/dist/chunk-M4I222LB.js.map +1 -0
- package/dist/chunk-Q7OSHSGZ.cjs +351 -0
- package/dist/chunk-Q7OSHSGZ.cjs.map +1 -0
- package/dist/codegen/cli.cjs +85 -0
- package/dist/codegen/cli.cjs.map +1 -0
- package/dist/codegen/cli.d.cts +1 -0
- package/dist/codegen/cli.d.ts +1 -0
- package/dist/codegen/cli.js +83 -0
- package/dist/codegen/cli.js.map +1 -0
- package/dist/codegen/index.cjs +24 -0
- package/dist/codegen/index.cjs.map +1 -0
- package/dist/codegen/index.d.cts +24 -0
- package/dist/codegen/index.d.ts +24 -0
- package/dist/codegen/index.js +3 -0
- package/dist/codegen/index.js.map +1 -0
- package/dist/index.cjs +87 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +26 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +81 -0
- package/dist/index.js.map +1 -0
- package/dist/react/index.cjs +123 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +30 -0
- package/dist/react/index.d.ts +30 -0
- package/dist/react/index.js +113 -0
- package/dist/react/index.js.map +1 -0
- package/dist/synapse-runtime.iife.global.js +1 -0
- package/dist/types-BP0SNrpo.d.cts +96 -0
- package/dist/types-BP0SNrpo.d.ts +96 -0
- package/dist/vite/index.cjs +49 -0
- package/dist/vite/index.cjs.map +1 -0
- package/dist/vite/index.d.cts +21 -0
- package/dist/vite/index.d.ts +21 -0
- package/dist/vite/index.js +47 -0
- package/dist/vite/index.js.map +1 -0
- package/package.json +89 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
// src/detection.ts
|
|
2
|
+
var DEFAULT_THEME = {
|
|
3
|
+
mode: "light",
|
|
4
|
+
primaryColor: "#6366f1",
|
|
5
|
+
tokens: {}
|
|
6
|
+
};
|
|
7
|
+
function detectHost(initResponse) {
|
|
8
|
+
const resp = initResponse;
|
|
9
|
+
const serverInfo = safeObj(resp?.serverInfo);
|
|
10
|
+
const serverName = typeof serverInfo?.name === "string" ? serverInfo.name : "unknown";
|
|
11
|
+
const protocolVersion = typeof resp?.protocolVersion === "string" ? resp.protocolVersion : "unknown";
|
|
12
|
+
const hostContext = safeObj(resp?.hostContext);
|
|
13
|
+
const theme = extractTheme(hostContext?.theme);
|
|
14
|
+
return {
|
|
15
|
+
isNimbleBrain: serverName === "nimblebrain",
|
|
16
|
+
serverName,
|
|
17
|
+
protocolVersion,
|
|
18
|
+
theme
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function extractTheme(raw) {
|
|
22
|
+
const obj = safeObj(raw);
|
|
23
|
+
if (!obj) return { ...DEFAULT_THEME };
|
|
24
|
+
const mode = obj.mode === "light" || obj.mode === "dark" ? obj.mode : DEFAULT_THEME.mode;
|
|
25
|
+
const primaryColor = typeof obj.primaryColor === "string" ? obj.primaryColor : DEFAULT_THEME.primaryColor;
|
|
26
|
+
const tokens = obj.tokens !== null && typeof obj.tokens === "object" && !Array.isArray(obj.tokens) ? obj.tokens : {};
|
|
27
|
+
return { mode, primaryColor, tokens };
|
|
28
|
+
}
|
|
29
|
+
function safeObj(value) {
|
|
30
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
return void 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/keyboard.ts
|
|
37
|
+
var KeyboardForwarder = class {
|
|
38
|
+
listener;
|
|
39
|
+
destroyed = false;
|
|
40
|
+
constructor(transport, customKeys) {
|
|
41
|
+
const config = customKeys ?? null;
|
|
42
|
+
this.listener = (event) => {
|
|
43
|
+
if (this.destroyed) return;
|
|
44
|
+
if (this.shouldForward(event, config)) {
|
|
45
|
+
event.preventDefault();
|
|
46
|
+
transport.send("ui/keydown", {
|
|
47
|
+
key: event.key,
|
|
48
|
+
ctrlKey: event.ctrlKey,
|
|
49
|
+
metaKey: event.metaKey,
|
|
50
|
+
shiftKey: event.shiftKey,
|
|
51
|
+
altKey: event.altKey
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
document.addEventListener("keydown", this.listener);
|
|
56
|
+
}
|
|
57
|
+
destroy() {
|
|
58
|
+
if (this.destroyed) return;
|
|
59
|
+
this.destroyed = true;
|
|
60
|
+
document.removeEventListener("keydown", this.listener);
|
|
61
|
+
}
|
|
62
|
+
shouldForward(event, config) {
|
|
63
|
+
if (config && config.length === 0) return false;
|
|
64
|
+
if (config) {
|
|
65
|
+
return config.some(
|
|
66
|
+
(k) => event.key.toLowerCase() === k.key.toLowerCase() && (k.ctrl === void 0 || event.ctrlKey === k.ctrl) && (k.meta === void 0 || event.metaKey === k.meta) && (k.shift === void 0 || event.shiftKey === k.shift) && (k.alt === void 0 || event.altKey === k.alt)
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
if (event.key === "Escape") return true;
|
|
70
|
+
if (event.ctrlKey || event.metaKey) return true;
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// src/result-parser.ts
|
|
76
|
+
function parseToolResult(raw) {
|
|
77
|
+
if (raw == null) {
|
|
78
|
+
return { data: null, isError: false };
|
|
79
|
+
}
|
|
80
|
+
if (isCallToolResult(raw)) {
|
|
81
|
+
return parseCallToolResult(raw);
|
|
82
|
+
}
|
|
83
|
+
return { data: raw, isError: false };
|
|
84
|
+
}
|
|
85
|
+
function isCallToolResult(value) {
|
|
86
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
return Array.isArray(value.content);
|
|
90
|
+
}
|
|
91
|
+
function isTextBlock(block) {
|
|
92
|
+
if (block === null || typeof block !== "object" || Array.isArray(block)) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
const obj = block;
|
|
96
|
+
return obj.type === "text" && typeof obj.text === "string";
|
|
97
|
+
}
|
|
98
|
+
function parseCallToolResult(result) {
|
|
99
|
+
const isError = result.isError === true;
|
|
100
|
+
const content = result.content;
|
|
101
|
+
if (content.length === 0) {
|
|
102
|
+
return { data: null, isError };
|
|
103
|
+
}
|
|
104
|
+
const firstText = content.find(isTextBlock);
|
|
105
|
+
if (!firstText) {
|
|
106
|
+
return { data: content, isError };
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
return { data: JSON.parse(firstText.text), isError };
|
|
110
|
+
} catch {
|
|
111
|
+
return { data: firstText.text, isError };
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// src/transport.ts
|
|
116
|
+
var SynapseTransport = class {
|
|
117
|
+
counter = 0;
|
|
118
|
+
destroyed = false;
|
|
119
|
+
pending = /* @__PURE__ */ new Map();
|
|
120
|
+
handlers = /* @__PURE__ */ new Map();
|
|
121
|
+
listener;
|
|
122
|
+
constructor() {
|
|
123
|
+
this.listener = (event) => this.handleMessage(event);
|
|
124
|
+
window.addEventListener("message", this.listener);
|
|
125
|
+
}
|
|
126
|
+
send(method, params) {
|
|
127
|
+
if (this.destroyed) return;
|
|
128
|
+
const msg = {
|
|
129
|
+
jsonrpc: "2.0",
|
|
130
|
+
method,
|
|
131
|
+
...params !== void 0 && { params }
|
|
132
|
+
};
|
|
133
|
+
window.parent.postMessage(msg, "*");
|
|
134
|
+
}
|
|
135
|
+
request(method, params) {
|
|
136
|
+
if (this.destroyed) {
|
|
137
|
+
return Promise.reject(new Error("Transport destroyed"));
|
|
138
|
+
}
|
|
139
|
+
const id = `syn-${++this.counter}`;
|
|
140
|
+
const msg = {
|
|
141
|
+
jsonrpc: "2.0",
|
|
142
|
+
method,
|
|
143
|
+
id,
|
|
144
|
+
...params !== void 0 && { params }
|
|
145
|
+
};
|
|
146
|
+
return new Promise((resolve, reject) => {
|
|
147
|
+
this.pending.set(id, { resolve, reject });
|
|
148
|
+
window.parent.postMessage(msg, "*");
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
onMessage(method, callback) {
|
|
152
|
+
if (!this.handlers.has(method)) {
|
|
153
|
+
this.handlers.set(method, /* @__PURE__ */ new Set());
|
|
154
|
+
}
|
|
155
|
+
this.handlers.get(method)?.add(callback);
|
|
156
|
+
return () => {
|
|
157
|
+
const set = this.handlers.get(method);
|
|
158
|
+
if (set) {
|
|
159
|
+
set.delete(callback);
|
|
160
|
+
if (set.size === 0) {
|
|
161
|
+
this.handlers.delete(method);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
destroy() {
|
|
167
|
+
if (this.destroyed) return;
|
|
168
|
+
this.destroyed = true;
|
|
169
|
+
window.removeEventListener("message", this.listener);
|
|
170
|
+
const error = new Error("Transport destroyed");
|
|
171
|
+
for (const entry of this.pending.values()) {
|
|
172
|
+
entry.reject(error);
|
|
173
|
+
}
|
|
174
|
+
this.pending.clear();
|
|
175
|
+
this.handlers.clear();
|
|
176
|
+
}
|
|
177
|
+
handleMessage(event) {
|
|
178
|
+
if (this.destroyed) return;
|
|
179
|
+
const data = event.data;
|
|
180
|
+
if (!data || data.jsonrpc !== "2.0") return;
|
|
181
|
+
if ("id" in data && data.id && !("method" in data)) {
|
|
182
|
+
const response = data;
|
|
183
|
+
const entry = this.pending.get(response.id);
|
|
184
|
+
if (!entry) return;
|
|
185
|
+
this.pending.delete(response.id);
|
|
186
|
+
if (response.error) {
|
|
187
|
+
const err = new Error(response.error.message);
|
|
188
|
+
err.code = response.error.code;
|
|
189
|
+
err.data = response.error.data;
|
|
190
|
+
entry.reject(err);
|
|
191
|
+
} else {
|
|
192
|
+
entry.resolve(response.result);
|
|
193
|
+
}
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if ("method" in data && !("id" in data && data.id)) {
|
|
197
|
+
const notification = data;
|
|
198
|
+
const set = this.handlers.get(notification.method);
|
|
199
|
+
if (set) {
|
|
200
|
+
for (const handler of set) {
|
|
201
|
+
handler(notification.params);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// src/core.ts
|
|
209
|
+
function createSynapse(options) {
|
|
210
|
+
const { name, version, internal = false, forwardKeys } = options;
|
|
211
|
+
const transport = new SynapseTransport();
|
|
212
|
+
let hostInfo = null;
|
|
213
|
+
let currentTheme = {
|
|
214
|
+
mode: "light",
|
|
215
|
+
primaryColor: "#6366f1",
|
|
216
|
+
tokens: {}
|
|
217
|
+
};
|
|
218
|
+
let destroyed = false;
|
|
219
|
+
let stateTimer = null;
|
|
220
|
+
let keyboard = null;
|
|
221
|
+
const ready = transport.request("ui/initialize", {
|
|
222
|
+
protocolVersion: "2026-01-26",
|
|
223
|
+
clientInfo: { name, version },
|
|
224
|
+
capabilities: {}
|
|
225
|
+
}).then((result) => {
|
|
226
|
+
hostInfo = detectHost(result);
|
|
227
|
+
currentTheme = hostInfo.theme;
|
|
228
|
+
transport.send("ui/notifications/initialized", {});
|
|
229
|
+
keyboard = new KeyboardForwarder(transport, forwardKeys);
|
|
230
|
+
});
|
|
231
|
+
const unsubTheme = transport.onMessage("ui/notifications/host-context-changed", (params) => {
|
|
232
|
+
if (!params) return;
|
|
233
|
+
const mode = params.theme === "dark" ? "dark" : "light";
|
|
234
|
+
const tokens = params.tokens && typeof params.tokens === "object" ? params.tokens : currentTheme.tokens;
|
|
235
|
+
currentTheme = { mode, primaryColor: currentTheme.primaryColor, tokens };
|
|
236
|
+
for (const cb of themeCallbacks) cb(currentTheme);
|
|
237
|
+
});
|
|
238
|
+
const unsubNbTheme = transport.onMessage("ui/themeChanged", (params) => {
|
|
239
|
+
if (!params) return;
|
|
240
|
+
const mode = params.mode === "dark" || params.mode === "light" ? params.mode : currentTheme.mode;
|
|
241
|
+
const tokens = params.tokens && typeof params.tokens === "object" ? params.tokens : currentTheme.tokens;
|
|
242
|
+
currentTheme = { mode, primaryColor: currentTheme.primaryColor, tokens };
|
|
243
|
+
for (const cb of themeCallbacks) cb(currentTheme);
|
|
244
|
+
});
|
|
245
|
+
const themeCallbacks = /* @__PURE__ */ new Set();
|
|
246
|
+
const dataCallbacks = /* @__PURE__ */ new Set();
|
|
247
|
+
const unsubData = transport.onMessage("ui/datachanged", (params) => {
|
|
248
|
+
if (!params) return;
|
|
249
|
+
const event = {
|
|
250
|
+
source: "agent",
|
|
251
|
+
server: params.server ?? "",
|
|
252
|
+
tool: params.tool ?? ""
|
|
253
|
+
};
|
|
254
|
+
for (const cb of dataCallbacks) cb(event);
|
|
255
|
+
});
|
|
256
|
+
const isNB = () => hostInfo?.isNimbleBrain === true;
|
|
257
|
+
const synapse = {
|
|
258
|
+
get ready() {
|
|
259
|
+
return ready;
|
|
260
|
+
},
|
|
261
|
+
get isNimbleBrainHost() {
|
|
262
|
+
return isNB();
|
|
263
|
+
},
|
|
264
|
+
async callTool(toolName, args) {
|
|
265
|
+
const params = {
|
|
266
|
+
name: toolName,
|
|
267
|
+
arguments: args ?? {}
|
|
268
|
+
};
|
|
269
|
+
if (internal) {
|
|
270
|
+
params.server = name;
|
|
271
|
+
}
|
|
272
|
+
const raw = await transport.request("tools/call", params);
|
|
273
|
+
return parseToolResult(raw);
|
|
274
|
+
},
|
|
275
|
+
onDataChanged(callback) {
|
|
276
|
+
dataCallbacks.add(callback);
|
|
277
|
+
return () => {
|
|
278
|
+
dataCallbacks.delete(callback);
|
|
279
|
+
};
|
|
280
|
+
},
|
|
281
|
+
getTheme() {
|
|
282
|
+
return { ...currentTheme };
|
|
283
|
+
},
|
|
284
|
+
onThemeChanged(callback) {
|
|
285
|
+
themeCallbacks.add(callback);
|
|
286
|
+
return () => {
|
|
287
|
+
themeCallbacks.delete(callback);
|
|
288
|
+
};
|
|
289
|
+
},
|
|
290
|
+
action(action, params) {
|
|
291
|
+
if (!isNB()) return;
|
|
292
|
+
transport.send("ui/action", { action, ...params });
|
|
293
|
+
},
|
|
294
|
+
chat(message, context) {
|
|
295
|
+
if (!isNB()) return;
|
|
296
|
+
transport.send("ui/chat", { message, context });
|
|
297
|
+
},
|
|
298
|
+
setVisibleState(state, summary) {
|
|
299
|
+
if (!isNB()) return;
|
|
300
|
+
if (stateTimer) clearTimeout(stateTimer);
|
|
301
|
+
stateTimer = setTimeout(() => {
|
|
302
|
+
transport.send("ui/stateChanged", {
|
|
303
|
+
state,
|
|
304
|
+
...summary !== void 0 && { summary }
|
|
305
|
+
});
|
|
306
|
+
stateTimer = null;
|
|
307
|
+
}, 250);
|
|
308
|
+
},
|
|
309
|
+
downloadFile(filename, content, mimeType) {
|
|
310
|
+
if (!isNB()) return;
|
|
311
|
+
const data = typeof content === "string" ? content : "[Blob content not serializable]";
|
|
312
|
+
transport.send("ui/downloadFile", {
|
|
313
|
+
data,
|
|
314
|
+
filename,
|
|
315
|
+
mimeType: mimeType ?? "application/octet-stream"
|
|
316
|
+
});
|
|
317
|
+
},
|
|
318
|
+
openLink(url) {
|
|
319
|
+
if (isNB()) {
|
|
320
|
+
transport.send("ui/openLink", { url });
|
|
321
|
+
} else {
|
|
322
|
+
window.open(url, "_blank", "noopener");
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
_onMessage(method, callback) {
|
|
326
|
+
return transport.onMessage(method, callback);
|
|
327
|
+
},
|
|
328
|
+
_request(method, params) {
|
|
329
|
+
return transport.request(method, params);
|
|
330
|
+
},
|
|
331
|
+
destroy() {
|
|
332
|
+
if (destroyed) return;
|
|
333
|
+
destroyed = true;
|
|
334
|
+
if (stateTimer) clearTimeout(stateTimer);
|
|
335
|
+
keyboard?.destroy();
|
|
336
|
+
unsubTheme();
|
|
337
|
+
unsubNbTheme();
|
|
338
|
+
unsubData();
|
|
339
|
+
themeCallbacks.clear();
|
|
340
|
+
dataCallbacks.clear();
|
|
341
|
+
transport.destroy();
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
return synapse;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export { createSynapse };
|
|
348
|
+
//# sourceMappingURL=chunk-JZC3VC2C.js.map
|
|
349
|
+
//# sourceMappingURL=chunk-JZC3VC2C.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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-JZC3VC2C.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 if (event.key === \"Escape\") return true;\n if (event.ctrlKey || event.metaKey) return true;\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 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\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 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 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 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 if (!isNB()) return;\n transport.send(\"ui/chat\", { message, context });\n },\n\n setVisibleState(state: Record<string, unknown>, summary?: string): void {\n if (!isNB()) return;\n\n // Debounce: 250ms\n if (stateTimer) clearTimeout(stateTimer);\n stateTimer = setTimeout(() => {\n transport.send(\"ui/stateChanged\", {\n state,\n ...(summary !== undefined && { summary }),\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/downloadFile\", {\n data,\n filename,\n mimeType: mimeType ?? \"application/octet-stream\",\n });\n },\n\n openLink(url: string): void {\n if (isNB()) {\n transport.send(\"ui/openLink\", { url });\n } else {\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 themeCallbacks.clear();\n dataCallbacks.clear();\n transport.destroy();\n },\n };\n\n return synapse;\n}\n"]}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
2
|
+
import { join, basename } from 'path';
|
|
3
|
+
|
|
4
|
+
// src/codegen/schema-reader.ts
|
|
5
|
+
function readFromManifest(manifestPath) {
|
|
6
|
+
if (!existsSync(manifestPath)) {
|
|
7
|
+
throw new Error(`Manifest not found: ${manifestPath}`);
|
|
8
|
+
}
|
|
9
|
+
const raw = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
10
|
+
const tools = raw.tools ?? [];
|
|
11
|
+
if (!Array.isArray(tools)) {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
return tools.map((t) => ({
|
|
15
|
+
name: t.name,
|
|
16
|
+
description: t.description,
|
|
17
|
+
inputSchema: t.inputSchema ?? {},
|
|
18
|
+
outputSchema: t.outputSchema
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
async function readFromServer(url) {
|
|
22
|
+
const response = await fetch(url, {
|
|
23
|
+
method: "POST",
|
|
24
|
+
headers: { "Content-Type": "application/json" },
|
|
25
|
+
body: JSON.stringify({
|
|
26
|
+
jsonrpc: "2.0",
|
|
27
|
+
method: "tools/list",
|
|
28
|
+
id: "codegen-1",
|
|
29
|
+
params: {}
|
|
30
|
+
})
|
|
31
|
+
});
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
throw new Error(`Server responded with ${response.status}: ${response.statusText}`);
|
|
34
|
+
}
|
|
35
|
+
const result = await response.json();
|
|
36
|
+
if (result.error) {
|
|
37
|
+
throw new Error(`Server error: ${result.error.message}`);
|
|
38
|
+
}
|
|
39
|
+
const tools = result.result?.tools ?? [];
|
|
40
|
+
return tools.map((t) => ({
|
|
41
|
+
name: t.name,
|
|
42
|
+
description: t.description,
|
|
43
|
+
inputSchema: t.inputSchema ?? {},
|
|
44
|
+
outputSchema: t.outputSchema
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
function readFromSchemaDir(dirPath) {
|
|
48
|
+
if (!existsSync(dirPath)) {
|
|
49
|
+
throw new Error(`Schema directory not found: ${dirPath}`);
|
|
50
|
+
}
|
|
51
|
+
const files = readdirSync(dirPath).filter((f) => f.endsWith(".schema.json"));
|
|
52
|
+
const tools = [];
|
|
53
|
+
for (const file of files) {
|
|
54
|
+
const schema = JSON.parse(readFileSync(join(dirPath, file), "utf-8"));
|
|
55
|
+
const entityName = basename(file, ".schema.json");
|
|
56
|
+
tools.push(
|
|
57
|
+
{
|
|
58
|
+
name: `create_${entityName}`,
|
|
59
|
+
description: `Create a new ${entityName}`,
|
|
60
|
+
inputSchema: schema,
|
|
61
|
+
outputSchema: schema
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: `read_${entityName}`,
|
|
65
|
+
description: `Read a ${entityName} by ID`,
|
|
66
|
+
inputSchema: {
|
|
67
|
+
type: "object",
|
|
68
|
+
properties: { id: { type: "string" } },
|
|
69
|
+
required: ["id"]
|
|
70
|
+
},
|
|
71
|
+
outputSchema: schema
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: `update_${entityName}`,
|
|
75
|
+
description: `Update an existing ${entityName}`,
|
|
76
|
+
inputSchema: schema,
|
|
77
|
+
outputSchema: schema
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: `delete_${entityName}`,
|
|
81
|
+
description: `Delete a ${entityName} by ID`,
|
|
82
|
+
inputSchema: {
|
|
83
|
+
type: "object",
|
|
84
|
+
properties: { id: { type: "string" } },
|
|
85
|
+
required: ["id"]
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: `list_${entityName}s`,
|
|
90
|
+
description: `List all ${entityName}s`,
|
|
91
|
+
inputSchema: {
|
|
92
|
+
type: "object",
|
|
93
|
+
properties: {
|
|
94
|
+
filter: { type: "string" },
|
|
95
|
+
limit: { type: "number" }
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
outputSchema: {
|
|
99
|
+
type: "object",
|
|
100
|
+
properties: {
|
|
101
|
+
items: { type: "array", items: schema },
|
|
102
|
+
total: { type: "number" }
|
|
103
|
+
},
|
|
104
|
+
required: ["items", "total"]
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return tools;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/codegen/type-generator.ts
|
|
113
|
+
function generateTypes(tools, appName) {
|
|
114
|
+
const lines = [];
|
|
115
|
+
const mapName = `${toPascalCase(appName)}ToolMap`;
|
|
116
|
+
lines.push("/**");
|
|
117
|
+
lines.push(` * Auto-generated by @nimblebrain/synapse codegen`);
|
|
118
|
+
lines.push(` * Source: ${appName}`);
|
|
119
|
+
lines.push(` *`);
|
|
120
|
+
lines.push(
|
|
121
|
+
` * DO NOT EDIT \u2014 regenerate with: npx synapse codegen --from-manifest ./manifest.json`
|
|
122
|
+
);
|
|
123
|
+
lines.push(` */`);
|
|
124
|
+
lines.push("");
|
|
125
|
+
const mapEntries = [];
|
|
126
|
+
for (const tool of tools) {
|
|
127
|
+
const baseName = toPascalCase(tool.name);
|
|
128
|
+
const inputName = `${baseName}Input`;
|
|
129
|
+
lines.push(schemaToInterface(inputName, tool.inputSchema));
|
|
130
|
+
lines.push("");
|
|
131
|
+
let outputName = "unknown";
|
|
132
|
+
if (tool.outputSchema) {
|
|
133
|
+
outputName = `${baseName}Output`;
|
|
134
|
+
lines.push(schemaToInterface(outputName, tool.outputSchema));
|
|
135
|
+
lines.push("");
|
|
136
|
+
}
|
|
137
|
+
mapEntries.push(` ${tool.name}: { input: ${inputName}; output: ${outputName} };`);
|
|
138
|
+
}
|
|
139
|
+
lines.push(`/** Tool type map for typed useCallTool */`);
|
|
140
|
+
lines.push(`export interface ${mapName} {`);
|
|
141
|
+
for (const entry of mapEntries) {
|
|
142
|
+
lines.push(entry);
|
|
143
|
+
}
|
|
144
|
+
lines.push(`}`);
|
|
145
|
+
lines.push("");
|
|
146
|
+
return lines.join("\n");
|
|
147
|
+
}
|
|
148
|
+
function schemaToInterface(name, schema) {
|
|
149
|
+
const type = schemaToType(schema, name);
|
|
150
|
+
if (schema.type === "object" || schema.properties) {
|
|
151
|
+
return generateInterface(name, schema);
|
|
152
|
+
}
|
|
153
|
+
return `export type ${name} = ${type};`;
|
|
154
|
+
}
|
|
155
|
+
function generateInterface(name, schema) {
|
|
156
|
+
const lines = [];
|
|
157
|
+
lines.push(`export interface ${name} {`);
|
|
158
|
+
const props = schema.properties ?? {};
|
|
159
|
+
const required = new Set(schema.required ?? []);
|
|
160
|
+
for (const [key, propSchema] of Object.entries(props)) {
|
|
161
|
+
const isRequired = required.has(key);
|
|
162
|
+
const type = schemaToType(propSchema, name + toPascalCase(key));
|
|
163
|
+
const desc = propSchema.description;
|
|
164
|
+
if (desc) {
|
|
165
|
+
lines.push(` /** ${desc} */`);
|
|
166
|
+
}
|
|
167
|
+
lines.push(` ${key}${isRequired ? "" : "?"}: ${type};`);
|
|
168
|
+
}
|
|
169
|
+
lines.push(`}`);
|
|
170
|
+
return lines.join("\n");
|
|
171
|
+
}
|
|
172
|
+
function schemaToType(schema, context) {
|
|
173
|
+
if (schema.enum && Array.isArray(schema.enum)) {
|
|
174
|
+
return schema.enum.map((v) => typeof v === "string" ? `"${v}"` : String(v)).join(" | ");
|
|
175
|
+
}
|
|
176
|
+
if (schema.oneOf && Array.isArray(schema.oneOf)) {
|
|
177
|
+
return schema.oneOf.map((s, i) => schemaToType(s, `${context}Option${i}`)).join(" | ");
|
|
178
|
+
}
|
|
179
|
+
if (schema.anyOf && Array.isArray(schema.anyOf)) {
|
|
180
|
+
return schema.anyOf.map((s, i) => schemaToType(s, `${context}Option${i}`)).join(" | ");
|
|
181
|
+
}
|
|
182
|
+
if (schema.allOf && Array.isArray(schema.allOf)) {
|
|
183
|
+
return schema.allOf.map((s, i) => schemaToType(s, `${context}Part${i}`)).join(" & ");
|
|
184
|
+
}
|
|
185
|
+
const type = schema.type;
|
|
186
|
+
if (Array.isArray(type)) {
|
|
187
|
+
return type.map((t) => primitiveType(t)).join(" | ");
|
|
188
|
+
}
|
|
189
|
+
switch (type) {
|
|
190
|
+
case "string":
|
|
191
|
+
return "string";
|
|
192
|
+
case "number":
|
|
193
|
+
case "integer":
|
|
194
|
+
return "number";
|
|
195
|
+
case "boolean":
|
|
196
|
+
return "boolean";
|
|
197
|
+
case "null":
|
|
198
|
+
return "null";
|
|
199
|
+
case "array": {
|
|
200
|
+
const items = schema.items;
|
|
201
|
+
if (items) {
|
|
202
|
+
return `${schemaToType(items, `${context}Item`)}[]`;
|
|
203
|
+
}
|
|
204
|
+
return "unknown[]";
|
|
205
|
+
}
|
|
206
|
+
case "object": {
|
|
207
|
+
if (schema.properties) {
|
|
208
|
+
const props = schema.properties;
|
|
209
|
+
const required = new Set(schema.required ?? []);
|
|
210
|
+
const fields = Object.entries(props).map(([key, propSchema]) => {
|
|
211
|
+
const t = schemaToType(propSchema, context + toPascalCase(key));
|
|
212
|
+
return `${key}${required.has(key) ? "" : "?"}: ${t}`;
|
|
213
|
+
}).join("; ");
|
|
214
|
+
return `{ ${fields} }`;
|
|
215
|
+
}
|
|
216
|
+
return "Record<string, unknown>";
|
|
217
|
+
}
|
|
218
|
+
default:
|
|
219
|
+
return "unknown";
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function primitiveType(t) {
|
|
223
|
+
switch (t) {
|
|
224
|
+
case "string":
|
|
225
|
+
return "string";
|
|
226
|
+
case "number":
|
|
227
|
+
case "integer":
|
|
228
|
+
return "number";
|
|
229
|
+
case "boolean":
|
|
230
|
+
return "boolean";
|
|
231
|
+
case "null":
|
|
232
|
+
return "null";
|
|
233
|
+
default:
|
|
234
|
+
return "unknown";
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
function toPascalCase(str) {
|
|
238
|
+
return str.split(/[-_@/]/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export { generateTypes, readFromManifest, readFromSchemaDir, readFromServer };
|
|
242
|
+
//# sourceMappingURL=chunk-M4I222LB.js.map
|
|
243
|
+
//# sourceMappingURL=chunk-M4I222LB.js.map
|