brightspace-mcp-server 1.5.1 → 1.6.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/README.md +2 -2
- package/build/api/cache.d.ts.map +1 -1
- package/build/api/cache.js +6 -0
- package/build/api/cache.js.map +1 -1
- package/build/api/client.d.ts +5 -0
- package/build/api/client.d.ts.map +1 -1
- package/build/api/client.js +57 -14
- package/build/api/client.js.map +1 -1
- package/build/api/paginate.d.ts +23 -0
- package/build/api/paginate.d.ts.map +1 -0
- package/build/api/paginate.js +94 -0
- package/build/api/paginate.js.map +1 -0
- package/build/api/retry.d.ts +42 -0
- package/build/api/retry.d.ts.map +1 -0
- package/build/api/retry.js +50 -0
- package/build/api/retry.js.map +1 -0
- package/build/api/types.d.ts +3 -0
- package/build/api/types.d.ts.map +1 -1
- package/build/api/types.js.map +1 -1
- package/build/auth/browser-auth.d.ts.map +1 -1
- package/build/auth/browser-auth.js +2 -1
- package/build/auth/browser-auth.js.map +1 -1
- package/build/auth/session-store.d.ts.map +1 -1
- package/build/auth/session-store.js +4 -4
- package/build/auth/session-store.js.map +1 -1
- package/build/index.js +3 -2
- package/build/index.js.map +1 -1
- package/build/tools/get-assignment-files.d.ts +12 -0
- package/build/tools/get-assignment-files.d.ts.map +1 -0
- package/build/tools/get-assignment-files.js +170 -0
- package/build/tools/get-assignment-files.js.map +1 -0
- package/build/tools/get-assignments.d.ts.map +1 -1
- package/build/tools/get-assignments.js +58 -27
- package/build/tools/get-assignments.js.map +1 -1
- package/build/tools/get-classlist-emails.d.ts.map +1 -1
- package/build/tools/get-classlist-emails.js +4 -6
- package/build/tools/get-classlist-emails.js.map +1 -1
- package/build/tools/get-course-content.d.ts.map +1 -1
- package/build/tools/get-course-content.js +12 -2
- package/build/tools/get-course-content.js.map +1 -1
- package/build/tools/get-my-courses.d.ts.map +1 -1
- package/build/tools/get-my-courses.js +4 -7
- package/build/tools/get-my-courses.js.map +1 -1
- package/build/tools/get-roster.d.ts.map +1 -1
- package/build/tools/get-roster.js +33 -24
- package/build/tools/get-roster.js.map +1 -1
- package/build/tools/index.d.ts +1 -0
- package/build/tools/index.d.ts.map +1 -1
- package/build/tools/index.js +1 -0
- package/build/tools/index.js.map +1 -1
- package/build/tools/schemas.d.ts +8 -0
- package/build/tools/schemas.d.ts.map +1 -1
- package/build/tools/schemas.js +14 -0
- package/build/tools/schemas.js.map +1 -1
- package/build/utils/atomic-write.d.ts +18 -0
- package/build/utils/atomic-write.d.ts.map +1 -0
- package/build/utils/atomic-write.js +84 -0
- package/build/utils/atomic-write.js.map +1 -0
- package/build/utils/config-store.d.ts.map +1 -1
- package/build/utils/config-store.js +2 -3
- package/build/utils/config-store.js.map +1 -1
- package/build/utils/logger.d.ts.map +1 -1
- package/build/utils/logger.js +45 -1
- package/build/utils/logger.js.map +1 -1
- package/build/utils/update-checker.d.ts +30 -5
- package/build/utils/update-checker.d.ts.map +1 -1
- package/build/utils/update-checker.js +79 -45
- package/build/utils/update-checker.js.map +1 -1
- package/build/utils/zip-extract.d.ts +15 -0
- package/build/utils/zip-extract.d.ts.map +1 -0
- package/build/utils/zip-extract.js +170 -0
- package/build/utils/zip-extract.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-store.js","sourceRoot":"","sources":["../../src/utils/config-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"config-store.js","sourceRoot":"","sources":["../../src/utils/config-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAgBxD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,kBAAkB,CAAC,CAAC;AAC/D,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAEzD,MAAM,UAAU,iBAAiB;IAC/B,OAAO,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAClD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAoB,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAuB;IACrD,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC;IAC/C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;IACvF,CAAC;IACD,mBAAmB,CACjB,WAAW,EACX,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EACtC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CACjC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,OAAO,WAAW,CAAC;AACrB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAWlD,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAEjD;
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAWlD,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAEjD;AA2ED,wBAAgB,GAAG,CACjB,KAAK,EAAE,QAAQ,EACf,OAAO,EAAE,MAAM,EACf,GAAG,IAAI,EAAE,OAAO,EAAE,GACjB,IAAI,CAMN;AAGD,wBAAgB,iBAAiB,IAAI,IAAI,CAOxC"}
|
package/build/utils/logger.js
CHANGED
|
@@ -16,22 +16,66 @@ export function setLogLevel(level) {
|
|
|
16
16
|
/**
|
|
17
17
|
* Redact sensitive patterns from log output.
|
|
18
18
|
* Tokens, passwords, and secrets are replaced with first 8 chars + "...REDACTED".
|
|
19
|
+
*
|
|
20
|
+
* Deliberately no blanket uppercase or base32 rule: it would eat course codes
|
|
21
|
+
* like ECE264, which are exactly what a useful log line contains.
|
|
19
22
|
*/
|
|
20
23
|
function redact(value) {
|
|
24
|
+
// JSON web tokens, wherever they appear
|
|
25
|
+
value = value.replace(/eyJ[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]*)?/g, "eyJ...REDACTED");
|
|
21
26
|
// Redact Bearer tokens
|
|
22
27
|
value = value.replace(/Bearer\s+([A-Za-z0-9._~+/=-]{8})[A-Za-z0-9._~+/=-]*/g, "Bearer $1...REDACTED");
|
|
23
28
|
// Redact cookie: prefixed tokens
|
|
24
29
|
value = value.replace(/cookie:([^\s]{8})[^\s]*/g, "cookie:$1...REDACTED");
|
|
30
|
+
// JSON-serialized header fields: {"Authorization":"..."}, {"Cookie":"..."}
|
|
31
|
+
value = value.replace(/("(?:authorization|cookie|set-cookie)"\s*:\s*")[^"]*(")/gi, "$1...REDACTED$2");
|
|
32
|
+
// Credentials embedded in a URL: scheme://user:pass@host
|
|
33
|
+
value = value.replace(/(\b[a-z][a-z0-9+.-]*:\/\/)[^\s/:@]+:[^\s/@]+@/gi, "$1***:***@");
|
|
25
34
|
// Redact anything that looks like a long token (40+ chars of base64-like)
|
|
26
35
|
value = value.replace(/([A-Za-z0-9._~+/=-]{40,})/g, (match) => match.substring(0, 8) + "...REDACTED");
|
|
27
36
|
return value;
|
|
28
37
|
}
|
|
38
|
+
const MAX_ARG_LENGTH = 2000;
|
|
39
|
+
/**
|
|
40
|
+
* Turn a log argument into text so the redactor can see inside it. Errors
|
|
41
|
+
* keep their name, message, and first stack frame; objects are serialized
|
|
42
|
+
* with cycles broken; everything else is stringified.
|
|
43
|
+
*/
|
|
44
|
+
function serializeArg(arg) {
|
|
45
|
+
if (arg instanceof Error) {
|
|
46
|
+
const frame = arg.stack?.split("\n").find((line) => line.trim().startsWith("at ")) ?? "";
|
|
47
|
+
return `${arg.name}: ${arg.message}${frame ? ` ${frame.trim()}` : ""}`;
|
|
48
|
+
}
|
|
49
|
+
if (typeof arg === "string")
|
|
50
|
+
return arg;
|
|
51
|
+
if (typeof arg === "object" && arg !== null) {
|
|
52
|
+
const seen = new WeakSet();
|
|
53
|
+
try {
|
|
54
|
+
const json = JSON.stringify(arg, (_key, value) => {
|
|
55
|
+
if (typeof value === "bigint")
|
|
56
|
+
return value.toString();
|
|
57
|
+
if (typeof value === "object" && value !== null) {
|
|
58
|
+
if (seen.has(value))
|
|
59
|
+
return "[Circular]";
|
|
60
|
+
seen.add(value);
|
|
61
|
+
}
|
|
62
|
+
return value;
|
|
63
|
+
});
|
|
64
|
+
return json.length > MAX_ARG_LENGTH ? `${json.slice(0, MAX_ARG_LENGTH)}...` : json;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return String(arg);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return String(arg);
|
|
71
|
+
}
|
|
29
72
|
export function log(level, message, ...args) {
|
|
30
73
|
if (LEVEL_ORDER[level] < LEVEL_ORDER[currentLevel])
|
|
31
74
|
return;
|
|
32
75
|
const timestamp = new Date().toISOString();
|
|
33
76
|
const safeMessage = redact(message);
|
|
34
|
-
|
|
77
|
+
const safeArgs = args.map((arg) => redact(serializeArg(arg)));
|
|
78
|
+
console.error(`[${timestamp}] [${level}] ${safeMessage}`, ...safeArgs);
|
|
35
79
|
}
|
|
36
80
|
// Override console.log in production to prevent accidental stdout writes
|
|
37
81
|
export function enableStdoutGuard() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,IAAI,YAAY,GAAa,MAAM,CAAC;AAEpC,MAAM,WAAW,GAA6B;IAC5C,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;CACT,CAAC;AAEF,MAAM,UAAU,WAAW,CAAC,KAAe;IACzC,YAAY,GAAG,KAAK,CAAC;AACvB,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Background npm update checker
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* Background npm update checker: look, and tell, but never touch.
|
|
3
|
+
*
|
|
4
|
+
* On startup the server asks the npm registry for the latest published
|
|
5
|
+
* version. If it is newer than the running one, the user is told how to
|
|
6
|
+
* update. Nothing is ever installed by this module. The one side effect it
|
|
7
|
+
* keeps is scoped to this package's own stale npx cache directories, because
|
|
8
|
+
* clearing them is what lets `npx brightspace-mcp-server@latest` actually
|
|
9
|
+
* pick up the new version on the next start.
|
|
10
|
+
*
|
|
11
|
+
* Set D2L_NO_UPDATE_CHECK to any value to switch the check off entirely.
|
|
6
12
|
*/
|
|
7
|
-
|
|
13
|
+
/**
|
|
14
|
+
* True only when `latest` is strictly newer than `installed`, compared as
|
|
15
|
+
* numeric major, minor, patch. Prerelease suffixes are ignored, and anything
|
|
16
|
+
* that does not parse as a version is never "newer", so a registry hiccup
|
|
17
|
+
* cannot announce an update that does not exist.
|
|
18
|
+
*/
|
|
19
|
+
export declare function isNewerVersion(latest: string, installed: string): boolean;
|
|
20
|
+
export interface UpdateCheckDeps {
|
|
21
|
+
fetchImpl?: typeof fetch;
|
|
22
|
+
env?: Record<string, string | undefined>;
|
|
23
|
+
installedVersion?: string;
|
|
24
|
+
runningFromNpxCache?: boolean;
|
|
25
|
+
clearCaches?: () => Promise<number>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Run one update check. Never throws and never blocks the caller for long:
|
|
29
|
+
* the registry request is bounded by a timeout and every failure is
|
|
30
|
+
* swallowed, because a version check must not affect the server.
|
|
31
|
+
*/
|
|
32
|
+
export declare function initUpdateChecker(deps?: UpdateCheckDeps): Promise<void>;
|
|
8
33
|
export declare function getUpdateNotice(): string | null;
|
|
9
34
|
//# sourceMappingURL=update-checker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-checker.d.ts","sourceRoot":"","sources":["../../src/utils/update-checker.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"update-checker.d.ts","sourceRoot":"","sources":["../../src/utils/update-checker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA8DH;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAQzE;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CACrC;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmCjF;AAED,wBAAgB,eAAe,IAAI,MAAM,GAAG,IAAI,CAI/C"}
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Background npm update checker
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* Background npm update checker: look, and tell, but never touch.
|
|
3
|
+
*
|
|
4
|
+
* On startup the server asks the npm registry for the latest published
|
|
5
|
+
* version. If it is newer than the running one, the user is told how to
|
|
6
|
+
* update. Nothing is ever installed by this module. The one side effect it
|
|
7
|
+
* keeps is scoped to this package's own stale npx cache directories, because
|
|
8
|
+
* clearing them is what lets `npx brightspace-mcp-server@latest` actually
|
|
9
|
+
* pick up the new version on the next start.
|
|
10
|
+
*
|
|
11
|
+
* Set D2L_NO_UPDATE_CHECK to any value to switch the check off entirely.
|
|
6
12
|
*/
|
|
7
|
-
import { exec } from "node:child_process";
|
|
8
13
|
import { readFileSync } from "node:fs";
|
|
9
|
-
import { rm } from "node:fs/promises";
|
|
14
|
+
import { access, readdir, rm } from "node:fs/promises";
|
|
15
|
+
import { homedir } from "node:os";
|
|
10
16
|
import { fileURLToPath } from "node:url";
|
|
11
17
|
import { dirname, resolve, sep } from "node:path";
|
|
18
|
+
const PACKAGE_NAME = "brightspace-mcp-server";
|
|
19
|
+
const REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
20
|
+
const REGISTRY_TIMEOUT_MS = 5000;
|
|
12
21
|
const __filename = fileURLToPath(import.meta.url);
|
|
13
22
|
const projectRoot = resolve(dirname(__filename), "..", "..");
|
|
14
23
|
let notice = null;
|
|
@@ -25,65 +34,90 @@ function isNpxCache() {
|
|
|
25
34
|
const normalized = projectRoot.split(sep).join("/");
|
|
26
35
|
return /[\\/]_npx[\\/][^\\/]+[\\/]node_modules[\\/]brightspace-mcp-server/.test(normalized);
|
|
27
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Remove every npx cache entry that holds a copy of this package, so the next
|
|
39
|
+
* `npx brightspace-mcp-server@latest` downloads the new version instead of
|
|
40
|
+
* reusing a stale one. Touches nothing outside those directories.
|
|
41
|
+
*/
|
|
28
42
|
async function clearAllNpxCaches() {
|
|
29
|
-
const { homedir } = await import("node:os");
|
|
30
43
|
const npxCacheRoot = resolve(homedir(), ".npm", "_npx");
|
|
31
|
-
const { readdir } = await import("node:fs/promises");
|
|
32
44
|
let cleared = 0;
|
|
33
45
|
try {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
const pkgPath = resolve(npxCacheRoot, entry, "node_modules", "brightspace-mcp-server");
|
|
46
|
+
for (const entry of await readdir(npxCacheRoot)) {
|
|
47
|
+
const entryDir = resolve(npxCacheRoot, entry);
|
|
37
48
|
try {
|
|
38
|
-
|
|
39
|
-
await
|
|
40
|
-
await rm(resolve(npxCacheRoot, entry), { recursive: true, force: true });
|
|
49
|
+
await access(resolve(entryDir, "node_modules", PACKAGE_NAME));
|
|
50
|
+
await rm(entryDir, { recursive: true, force: true });
|
|
41
51
|
cleared++;
|
|
42
52
|
}
|
|
43
53
|
catch {
|
|
44
|
-
// Not
|
|
54
|
+
// Not one of ours, leave it alone.
|
|
45
55
|
}
|
|
46
56
|
}
|
|
47
57
|
}
|
|
48
58
|
catch {
|
|
49
|
-
// npx cache
|
|
59
|
+
// No npx cache, or not readable. Nothing to clear.
|
|
50
60
|
}
|
|
51
61
|
return cleared;
|
|
52
62
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
63
|
+
function parseTriple(version) {
|
|
64
|
+
const match = /^v?(\d+)\.(\d+)\.(\d+)/.exec(version.trim());
|
|
65
|
+
if (!match)
|
|
66
|
+
return null;
|
|
67
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* True only when `latest` is strictly newer than `installed`, compared as
|
|
71
|
+
* numeric major, minor, patch. Prerelease suffixes are ignored, and anything
|
|
72
|
+
* that does not parse as a version is never "newer", so a registry hiccup
|
|
73
|
+
* cannot announce an update that does not exist.
|
|
74
|
+
*/
|
|
75
|
+
export function isNewerVersion(latest, installed) {
|
|
76
|
+
const a = parseTriple(latest);
|
|
77
|
+
const b = parseTriple(installed);
|
|
78
|
+
if (!a || !b)
|
|
79
|
+
return false;
|
|
80
|
+
for (let i = 0; i < 3; i++) {
|
|
81
|
+
if (a[i] !== b[i])
|
|
82
|
+
return a[i] > b[i];
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Run one update check. Never throws and never blocks the caller for long:
|
|
88
|
+
* the registry request is bounded by a timeout and every failure is
|
|
89
|
+
* swallowed, because a version check must not affect the server.
|
|
90
|
+
*/
|
|
91
|
+
export async function initUpdateChecker(deps = {}) {
|
|
92
|
+
const { fetchImpl = fetch, env = process.env, installedVersion = getInstalledVersion(), runningFromNpxCache = isNpxCache(), clearCaches = clearAllNpxCaches, } = deps;
|
|
93
|
+
if (env.D2L_NO_UPDATE_CHECK)
|
|
94
|
+
return;
|
|
95
|
+
try {
|
|
96
|
+
const response = await fetchImpl(REGISTRY_URL, {
|
|
97
|
+
headers: { accept: "application/json" },
|
|
98
|
+
signal: AbortSignal.timeout(REGISTRY_TIMEOUT_MS),
|
|
99
|
+
});
|
|
100
|
+
if (!response.ok)
|
|
59
101
|
return;
|
|
60
|
-
const latest =
|
|
61
|
-
if (
|
|
102
|
+
const latest = (await response.json()).version;
|
|
103
|
+
if (typeof latest !== "string" || !isNewerVersion(latest, installedVersion))
|
|
62
104
|
return;
|
|
63
|
-
if (
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
`
|
|
68
|
-
|
|
69
|
-
})
|
|
70
|
-
.catch(() => {
|
|
71
|
-
notice = FALLBACK_MSG(installed, latest);
|
|
72
|
-
});
|
|
105
|
+
if (runningFromNpxCache) {
|
|
106
|
+
const count = await clearCaches();
|
|
107
|
+
notice =
|
|
108
|
+
`Update available: v${installedVersion} to v${latest}. ` +
|
|
109
|
+
`Cleared ${count} stale npx cache director${count === 1 ? "y" : "ies"} for ${PACKAGE_NAME} ` +
|
|
110
|
+
`so the next start downloads v${latest}. Restart your MCP client to pick it up.`;
|
|
73
111
|
}
|
|
74
112
|
else {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
notice =
|
|
81
|
-
`Auto-updated from v${installed} to v${latest}. ` +
|
|
82
|
-
"Restart your MCP client to use the new version.";
|
|
83
|
-
}
|
|
84
|
-
});
|
|
113
|
+
notice =
|
|
114
|
+
`Update available: v${installedVersion} to v${latest}. ` +
|
|
115
|
+
`Run: npx ${PACKAGE_NAME}@latest, or npm install -g ${PACKAGE_NAME}@latest`;
|
|
85
116
|
}
|
|
86
|
-
}
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// A version check must never take the server down.
|
|
120
|
+
}
|
|
87
121
|
}
|
|
88
122
|
export function getUpdateNotice() {
|
|
89
123
|
const result = notice;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-checker.js","sourceRoot":"","sources":["../../src/utils/update-checker.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"update-checker.js","sourceRoot":"","sources":["../../src/utils/update-checker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Purdue Brightspace MCP Server
|
|
3
|
+
* Copyright (c) 2026 Rohan Muppa. All rights reserved.
|
|
4
|
+
* Licensed under MIT. See LICENSE file for details.
|
|
5
|
+
*/
|
|
6
|
+
/** One entry from a ZIP archive by exact name, or null. Never throws. */
|
|
7
|
+
export declare function extractZipEntry(buffer: Buffer, entryName: string): Buffer | null;
|
|
8
|
+
/** Every entry name in the archive, or null when it does not parse. */
|
|
9
|
+
export declare function listZipEntries(buffer: Buffer): string[] | null;
|
|
10
|
+
/**
|
|
11
|
+
* Readable text from a DOCX, XLSX, or PPTX buffer, or null when the archive
|
|
12
|
+
* is not an Office document or carries nothing readable.
|
|
13
|
+
*/
|
|
14
|
+
export declare function officeDocumentText(buffer: Buffer): string | null;
|
|
15
|
+
//# sourceMappingURL=zip-extract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zip-extract.d.ts","sourceRoot":"","sources":["../../src/utils/zip-extract.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA0GH,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUhF;AAED,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAM9D;AAuBD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAuBhE"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Purdue Brightspace MCP Server
|
|
3
|
+
* Copyright (c) 2026 Rohan Muppa. All rights reserved.
|
|
4
|
+
* Licensed under MIT. See LICENSE file for details.
|
|
5
|
+
*/
|
|
6
|
+
import { inflateRawSync } from "node:zlib";
|
|
7
|
+
/**
|
|
8
|
+
* A dependency-free reader for the one thing we need out of a ZIP: a single
|
|
9
|
+
* named entry. DOCX, XLSX, and PPTX are all ZIP archives, and on a live course
|
|
10
|
+
* the assignment attachments were a DOCX and an XLSX, so reading them is what
|
|
11
|
+
* makes an attachment useful rather than just listed.
|
|
12
|
+
*
|
|
13
|
+
* The central directory is the index, and it is what this walks. Scanning the
|
|
14
|
+
* file for local header magic is the obvious shortcut and it is wrong: those
|
|
15
|
+
* four bytes can appear inside compressed content, and then the reader is
|
|
16
|
+
* parsing a header that does not exist.
|
|
17
|
+
*
|
|
18
|
+
* Nothing here throws. A truncated download, an unsupported compression
|
|
19
|
+
* method, or a ZIP64 archive all return null, because the caller's fallback
|
|
20
|
+
* is simply to report the file without its text.
|
|
21
|
+
*/
|
|
22
|
+
const EOCD_SIGNATURE = 0x06054b50;
|
|
23
|
+
const CENTRAL_FILE_SIGNATURE = 0x02014b50;
|
|
24
|
+
const EOCD_MIN_SIZE = 22;
|
|
25
|
+
const EOCD_MAX_COMMENT = 0xffff;
|
|
26
|
+
const ZIP64_MARKER = 0xffffffff;
|
|
27
|
+
const METHOD_STORED = 0;
|
|
28
|
+
const METHOD_DEFLATE = 8;
|
|
29
|
+
/** Find the End of Central Directory record by scanning backwards. */
|
|
30
|
+
function findEocd(buffer) {
|
|
31
|
+
const earliest = Math.max(0, buffer.length - EOCD_MIN_SIZE - EOCD_MAX_COMMENT);
|
|
32
|
+
for (let i = buffer.length - EOCD_MIN_SIZE; i >= earliest; i--) {
|
|
33
|
+
if (buffer.readUInt32LE(i) === EOCD_SIGNATURE)
|
|
34
|
+
return i;
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
function readCentralDirectory(buffer) {
|
|
39
|
+
const eocd = findEocd(buffer);
|
|
40
|
+
if (eocd === null)
|
|
41
|
+
return null;
|
|
42
|
+
const count = buffer.readUInt16LE(eocd + 10);
|
|
43
|
+
let offset = buffer.readUInt32LE(eocd + 16);
|
|
44
|
+
if (offset === ZIP64_MARKER)
|
|
45
|
+
return null; // ZIP64 is out of scope.
|
|
46
|
+
const entries = [];
|
|
47
|
+
for (let i = 0; i < count; i++) {
|
|
48
|
+
if (offset + 46 > buffer.length)
|
|
49
|
+
return null;
|
|
50
|
+
if (buffer.readUInt32LE(offset) !== CENTRAL_FILE_SIGNATURE)
|
|
51
|
+
return null;
|
|
52
|
+
const method = buffer.readUInt16LE(offset + 10);
|
|
53
|
+
const compressedSize = buffer.readUInt32LE(offset + 20);
|
|
54
|
+
const uncompressedSize = buffer.readUInt32LE(offset + 24);
|
|
55
|
+
const nameLength = buffer.readUInt16LE(offset + 28);
|
|
56
|
+
const extraLength = buffer.readUInt16LE(offset + 30);
|
|
57
|
+
const commentLength = buffer.readUInt16LE(offset + 32);
|
|
58
|
+
const localHeaderOffset = buffer.readUInt32LE(offset + 42);
|
|
59
|
+
const nameStart = offset + 46;
|
|
60
|
+
if (nameStart + nameLength > buffer.length)
|
|
61
|
+
return null;
|
|
62
|
+
entries.push({
|
|
63
|
+
name: buffer.subarray(nameStart, nameStart + nameLength).toString("utf-8"),
|
|
64
|
+
method,
|
|
65
|
+
compressedSize,
|
|
66
|
+
uncompressedSize,
|
|
67
|
+
localHeaderOffset,
|
|
68
|
+
});
|
|
69
|
+
offset = nameStart + nameLength + extraLength + commentLength;
|
|
70
|
+
}
|
|
71
|
+
return entries;
|
|
72
|
+
}
|
|
73
|
+
function readEntryData(buffer, entry) {
|
|
74
|
+
const header = entry.localHeaderOffset;
|
|
75
|
+
if (header + 30 > buffer.length)
|
|
76
|
+
return null;
|
|
77
|
+
// The local header repeats the name and extra lengths, and its extra field
|
|
78
|
+
// can differ in length from the central one, so read them from here.
|
|
79
|
+
const nameLength = buffer.readUInt16LE(header + 26);
|
|
80
|
+
const extraLength = buffer.readUInt16LE(header + 28);
|
|
81
|
+
const start = header + 30 + nameLength + extraLength;
|
|
82
|
+
const end = start + entry.compressedSize;
|
|
83
|
+
if (end > buffer.length)
|
|
84
|
+
return null;
|
|
85
|
+
const raw = buffer.subarray(start, end);
|
|
86
|
+
if (entry.method === METHOD_STORED)
|
|
87
|
+
return Buffer.from(raw);
|
|
88
|
+
if (entry.method !== METHOD_DEFLATE)
|
|
89
|
+
return null;
|
|
90
|
+
try {
|
|
91
|
+
return inflateRawSync(raw);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** One entry from a ZIP archive by exact name, or null. Never throws. */
|
|
98
|
+
export function extractZipEntry(buffer, entryName) {
|
|
99
|
+
try {
|
|
100
|
+
const entries = readCentralDirectory(buffer);
|
|
101
|
+
if (!entries)
|
|
102
|
+
return null;
|
|
103
|
+
const entry = entries.find((e) => e.name === entryName);
|
|
104
|
+
if (!entry)
|
|
105
|
+
return null;
|
|
106
|
+
return readEntryData(buffer, entry);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/** Every entry name in the archive, or null when it does not parse. */
|
|
113
|
+
export function listZipEntries(buffer) {
|
|
114
|
+
try {
|
|
115
|
+
return readCentralDirectory(buffer)?.map((e) => e.name) ?? null;
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const XML_ENTITIES = {
|
|
122
|
+
"&": "&",
|
|
123
|
+
"<": "<",
|
|
124
|
+
">": ">",
|
|
125
|
+
""": '"',
|
|
126
|
+
"'": "'",
|
|
127
|
+
"'": "'",
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Strip XML markup down to the words a reader cares about. Paragraph and
|
|
131
|
+
* cell boundaries become spaces so words from adjacent runs do not fuse.
|
|
132
|
+
*/
|
|
133
|
+
function xmlToText(xml) {
|
|
134
|
+
return xml
|
|
135
|
+
.replace(/<[^>]+>/g, " ")
|
|
136
|
+
.replace(/&(?:amp|lt|gt|quot|apos|#39);/g, (m) => XML_ENTITIES[m] ?? m)
|
|
137
|
+
.replace(/\s+/g, " ")
|
|
138
|
+
.trim();
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Readable text from a DOCX, XLSX, or PPTX buffer, or null when the archive
|
|
142
|
+
* is not an Office document or carries nothing readable.
|
|
143
|
+
*/
|
|
144
|
+
export function officeDocumentText(buffer) {
|
|
145
|
+
const names = listZipEntries(buffer);
|
|
146
|
+
if (!names)
|
|
147
|
+
return null;
|
|
148
|
+
const read = (name) => xmlToText(extractZipEntry(buffer, name)?.toString("utf-8") ?? "");
|
|
149
|
+
let text = "";
|
|
150
|
+
if (names.includes("word/document.xml")) {
|
|
151
|
+
text = read("word/document.xml");
|
|
152
|
+
}
|
|
153
|
+
else if (names.includes("xl/sharedStrings.xml")) {
|
|
154
|
+
text = read("xl/sharedStrings.xml");
|
|
155
|
+
}
|
|
156
|
+
else if (names.some((n) => n.startsWith("ppt/slides/slide"))) {
|
|
157
|
+
const slides = names
|
|
158
|
+
.filter((n) => /^ppt\/slides\/slide\d+\.xml$/.test(n))
|
|
159
|
+
.sort((a, b) => slideNumber(a) - slideNumber(b));
|
|
160
|
+
text = slides.map(read).filter(Boolean).join("\n\n");
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
return text.length > 0 ? text : null;
|
|
166
|
+
}
|
|
167
|
+
function slideNumber(name) {
|
|
168
|
+
return Number(/slide(\d+)\.xml$/.exec(name)?.[1] ?? 0);
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=zip-extract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zip-extract.js","sourceRoot":"","sources":["../../src/utils/zip-extract.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brightspace-mcp-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"mcpName": "io.github.rohanmuppa/brightspace",
|
|
5
5
|
"description": "MCP server for Brightspace (D2L). Check grades, due dates, assignments, announcements, syllabus, rosters and more via Claude, ChatGPT, Cursor, Windsurf, or any MCP client.",
|
|
6
6
|
"type": "module",
|