m365connector 0.3.5 → 0.3.7
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/README.md +29 -103
- package/package.json +4 -3
- package/src/browser-session.js +619 -0
- package/src/index.js +31 -14
- package/src/lib/content-reader.js +2746 -0
- package/src/lib/search-api.js +989 -0
- package/src/lib/vendor/jszip.min.js +13 -0
- package/src/token-providers.js +739 -0
- package/src/tools/search.js +12 -32
- package/src/ws-server.js +0 -203
package/src/tools/search.js
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
|
|
3
|
-
const WHOAMI_TIMEOUT_MS = 60000;
|
|
4
|
-
const FIND_PEOPLE_TIMEOUT_MS = 90000;
|
|
5
|
-
const COUNT_PEOPLE_TIMEOUT_MS = 120000;
|
|
6
|
-
|
|
7
3
|
const SEARCH_TOOL = {
|
|
8
4
|
name: "search",
|
|
9
5
|
description:
|
|
@@ -247,10 +243,9 @@ export function registerTools(registry, context) {
|
|
|
247
243
|
const size = Number.isInteger(args.size) ? args.size : 10;
|
|
248
244
|
const from = Number.isInteger(args.from) ? args.from : 0;
|
|
249
245
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
{ conversation_id: conversationId, query, source, connector, size, from }
|
|
253
|
-
{ timeoutMs: 30000 }
|
|
246
|
+
await context.substrateTokenProvider.getTokenEntry();
|
|
247
|
+
const wsResult = await context.searchApi.search(
|
|
248
|
+
{ conversation_id: conversationId, query, source, connector, size, from }
|
|
254
249
|
);
|
|
255
250
|
|
|
256
251
|
const resultObject = asObject(wsResult);
|
|
@@ -330,10 +325,8 @@ export function registerTools(registry, context) {
|
|
|
330
325
|
throw new Error("Invalid or expired read_handle. Run search again.");
|
|
331
326
|
}
|
|
332
327
|
|
|
333
|
-
const wsResult = await context.
|
|
334
|
-
|
|
335
|
-
{ conversation_id: conversationId, context: readContext, fullContent: Boolean(args.fullContent) },
|
|
336
|
-
{ timeoutMs: args.fullContent ? 90000 : 60000 }
|
|
328
|
+
const wsResult = await context.contentReader.read(
|
|
329
|
+
{ conversation_id: conversationId, context: readContext, fullContent: Boolean(args.fullContent) }
|
|
337
330
|
);
|
|
338
331
|
|
|
339
332
|
const result = asObject(wsResult);
|
|
@@ -407,16 +400,11 @@ export function registerTools(registry, context) {
|
|
|
407
400
|
throw new Error("conversation_id is required");
|
|
408
401
|
}
|
|
409
402
|
|
|
410
|
-
const wsResult = await context.
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
? args.direct_reports_size
|
|
416
|
-
: 30
|
|
417
|
-
},
|
|
418
|
-
{ timeoutMs: WHOAMI_TIMEOUT_MS }
|
|
419
|
-
);
|
|
403
|
+
const wsResult = await context.contentReader.whoAmI({
|
|
404
|
+
directReportsSize: Number.isInteger(args.direct_reports_size)
|
|
405
|
+
? args.direct_reports_size
|
|
406
|
+
: 30
|
|
407
|
+
});
|
|
420
408
|
|
|
421
409
|
return asObject(wsResult);
|
|
422
410
|
});
|
|
@@ -440,11 +428,7 @@ export function registerTools(registry, context) {
|
|
|
440
428
|
};
|
|
441
429
|
const size = Number.isInteger(args.size) ? args.size : 20;
|
|
442
430
|
|
|
443
|
-
const wsResult = await context.
|
|
444
|
-
"find_people",
|
|
445
|
-
{ conversation_id: conversationId, ...requestParams, size },
|
|
446
|
-
{ timeoutMs: FIND_PEOPLE_TIMEOUT_MS }
|
|
447
|
-
);
|
|
431
|
+
const wsResult = await context.contentReader.findPeople({ ...requestParams, size });
|
|
448
432
|
|
|
449
433
|
return asObject(wsResult);
|
|
450
434
|
});
|
|
@@ -465,11 +449,7 @@ export function registerTools(registry, context) {
|
|
|
465
449
|
title: typeof args.title === "string" ? args.title.trim() : ""
|
|
466
450
|
};
|
|
467
451
|
|
|
468
|
-
const wsResult = await context.
|
|
469
|
-
"count_people",
|
|
470
|
-
{ conversation_id: conversationId, ...requestParams },
|
|
471
|
-
{ timeoutMs: COUNT_PEOPLE_TIMEOUT_MS }
|
|
472
|
-
);
|
|
452
|
+
const wsResult = await context.contentReader.countPeople(requestParams);
|
|
473
453
|
|
|
474
454
|
return asObject(wsResult);
|
|
475
455
|
});
|
package/src/ws-server.js
DELETED
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
import crypto from "node:crypto";
|
|
2
|
-
import { WebSocket, WebSocketServer } from "ws";
|
|
3
|
-
|
|
4
|
-
export class WsServer {
|
|
5
|
-
constructor(options = {}) {
|
|
6
|
-
const stderrLogger = {
|
|
7
|
-
info: (...args) => console.error(...args),
|
|
8
|
-
warn: (...args) => console.warn(...args),
|
|
9
|
-
error: (...args) => console.error(...args)
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
this.host = options.host || "127.0.0.1";
|
|
13
|
-
this.port = Number(options.port || 52365);
|
|
14
|
-
this.logger = options.logger || stderrLogger;
|
|
15
|
-
|
|
16
|
-
this.wss = null;
|
|
17
|
-
this.client = null;
|
|
18
|
-
this.pending = new Map();
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
async start() {
|
|
22
|
-
if (this.wss) {
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
this.wss = new WebSocketServer({
|
|
27
|
-
host: this.host,
|
|
28
|
-
port: this.port
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
this.wss.on("connection", (socket, req) => {
|
|
32
|
-
this.handleConnection(socket, req).catch((err) => {
|
|
33
|
-
this.logger.error("[m365connector/ws] connection handling failed:", err);
|
|
34
|
-
try {
|
|
35
|
-
socket.close(1011, "internal error");
|
|
36
|
-
} catch (_err) {
|
|
37
|
-
// ignored
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
await new Promise((resolve, reject) => {
|
|
43
|
-
this.wss.once("listening", resolve);
|
|
44
|
-
this.wss.once("error", reject);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
this.logger.info(`[m365connector/ws] listening on ws://${this.host}:${this.port}`);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async stop() {
|
|
51
|
-
for (const pending of this.pending.values()) {
|
|
52
|
-
clearTimeout(pending.timeout);
|
|
53
|
-
pending.reject(new Error("WebSocket server stopped"));
|
|
54
|
-
}
|
|
55
|
-
this.pending.clear();
|
|
56
|
-
|
|
57
|
-
if (this.client) {
|
|
58
|
-
try {
|
|
59
|
-
this.client.close(1001, "server stopping");
|
|
60
|
-
} catch (_err) {
|
|
61
|
-
// ignored
|
|
62
|
-
}
|
|
63
|
-
this.client = null;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (!this.wss) {
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const wss = this.wss;
|
|
71
|
-
this.wss = null;
|
|
72
|
-
await new Promise((resolve) => wss.close(resolve));
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
isReady() {
|
|
76
|
-
return Boolean(this.client && this.client.readyState === WebSocket.OPEN);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
getStatus() {
|
|
80
|
-
return {
|
|
81
|
-
host: this.host,
|
|
82
|
-
port: this.port,
|
|
83
|
-
connected: this.isReady()
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
async sendRequest(type, params, options = {}) {
|
|
88
|
-
const timeoutMs = Number(options.timeoutMs || 30000);
|
|
89
|
-
|
|
90
|
-
if (!this.isReady()) {
|
|
91
|
-
throw new Error("M365 Connector extension is not connected");
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const id = crypto.randomUUID();
|
|
95
|
-
const payload = { id, type, params: params || {} };
|
|
96
|
-
|
|
97
|
-
return new Promise((resolve, reject) => {
|
|
98
|
-
const timeout = setTimeout(() => {
|
|
99
|
-
this.logger.warn(`[m365connector/ws] request timed out: type=${type} id=${id}`);
|
|
100
|
-
this.pending.delete(id);
|
|
101
|
-
reject(new Error(`WebSocket request timed out after ${timeoutMs}ms`));
|
|
102
|
-
}, timeoutMs);
|
|
103
|
-
|
|
104
|
-
this.pending.set(id, { resolve, reject, timeout });
|
|
105
|
-
|
|
106
|
-
try {
|
|
107
|
-
this.client.send(JSON.stringify(payload));
|
|
108
|
-
} catch (err) {
|
|
109
|
-
clearTimeout(timeout);
|
|
110
|
-
this.pending.delete(id);
|
|
111
|
-
reject(err);
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
async handleConnection(socket, req) {
|
|
117
|
-
const origin = req.headers.origin;
|
|
118
|
-
|
|
119
|
-
if (!origin) {
|
|
120
|
-
this.logger.warn("[m365connector/ws] rejected connection: missing origin header");
|
|
121
|
-
socket.close(1008, "origin required");
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (!origin.startsWith("chrome-extension://")) {
|
|
126
|
-
this.logger.warn(`[m365connector/ws] rejected connection from origin: ${origin}`);
|
|
127
|
-
socket.close(1008, "origin not allowed");
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (this.client && this.client.readyState === WebSocket.OPEN) {
|
|
132
|
-
// MV3 service workers can die silently without sending a close frame.
|
|
133
|
-
// When the extension reconnects, replace the stale connection.
|
|
134
|
-
this.logger.info("[m365connector/ws] replacing existing connection with new one");
|
|
135
|
-
try { this.client.terminate(); } catch (_e) { /* ignored */ }
|
|
136
|
-
this.handleClose(this.client);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Clean up if client ref exists but is no longer OPEN
|
|
140
|
-
if (this.client && this.client.readyState !== WebSocket.OPEN) {
|
|
141
|
-
this.client = null;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
this.client = socket;
|
|
145
|
-
|
|
146
|
-
socket.on("message", async (rawData) => {
|
|
147
|
-
await this.handleMessage(socket, rawData);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
socket.on("close", (code, reason) => {
|
|
151
|
-
this.logger.info(`[m365connector/ws] socket close: code=${code}, reason=${String(reason)}`);
|
|
152
|
-
this.handleClose(socket);
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
socket.on("error", (err) => {
|
|
156
|
-
this.logger.warn("[m365connector/ws] client socket error:", err.message);
|
|
157
|
-
});
|
|
158
|
-
this.logger.info("[m365connector/ws] extension connected");
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
async handleMessage(socket, rawData) {
|
|
162
|
-
let message;
|
|
163
|
-
try {
|
|
164
|
-
message = JSON.parse(String(rawData));
|
|
165
|
-
} catch (_err) {
|
|
166
|
-
socket.close(4002, "invalid json");
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (!message?.id) {
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const pending = this.pending.get(message.id);
|
|
175
|
-
if (!pending) {
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
clearTimeout(pending.timeout);
|
|
180
|
-
this.pending.delete(message.id);
|
|
181
|
-
|
|
182
|
-
if (!message.success) {
|
|
183
|
-
pending.reject(new Error(message.error || "Unknown extension error"));
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
pending.resolve(message.data);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
handleClose(socket) {
|
|
191
|
-
if (this.client === socket) {
|
|
192
|
-
this.client = null;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
for (const [id, pending] of this.pending.entries()) {
|
|
196
|
-
clearTimeout(pending.timeout);
|
|
197
|
-
pending.reject(new Error("Extension disconnected"));
|
|
198
|
-
this.pending.delete(id);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
this.logger.info("[m365connector/ws] extension disconnected");
|
|
202
|
-
}
|
|
203
|
-
}
|