neuro-canvas-lms 2.10.1 → 2.11.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/AGENTS.md +4 -3
- package/installer/cli.mjs +46 -43
- package/installer/lib.mjs +1 -1
- package/installer/messages.mjs +30 -5
- package/installer/verify.mjs +61 -0
- package/package.json +2 -2
- package/version.mjs +1 -1
package/AGENTS.md
CHANGED
|
@@ -30,6 +30,7 @@ Code map:
|
|
|
30
30
|
- `owner.mjs`: owner binding, allowed OAuth clients. `i18n.mjs` + `pages.mjs`: web pages.
|
|
31
31
|
- `update-check.mjs`: once a day reads the newest version number from npm; when it is newer the AI
|
|
32
32
|
tells the user how to update. Also the `canvas_connector_version` tool.
|
|
33
|
-
- `installer/`: the setup wizard
|
|
34
|
-
settings
|
|
35
|
-
|
|
33
|
+
- `installer/`: the setup wizard. Re-running it asks once (y/n), then updates with the saved
|
|
34
|
+
settings (without a terminal it does not ask). `installer/verify.mjs`: the post-deploy online
|
|
35
|
+
check, also run alone by `npx -y neuro-canvas-lms check`.
|
|
36
|
+
- `version.mjs`: shown on the home page and /healthz, compared with npm by update-check.mjs.
|
package/installer/cli.mjs
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
// Neuro-CanvasLMS installer. Deploys THIS copy of the code to your own Cloudflare account.
|
|
2
2
|
// npx -y neuro-canvas-lms install, or update an existing install
|
|
3
3
|
// npx -y neuro-canvas-lms uninstall remove it
|
|
4
|
+
// npx -y neuro-canvas-lms check only check an installed connector (deploys nothing)
|
|
4
5
|
// (from a clone: npm run setup / npm run setup -- uninstall)
|
|
5
6
|
// Running it again updates the connector to the code you are running and keeps its storage, so
|
|
6
7
|
// existing Claude/ChatGPT connections keep working.
|
|
7
8
|
// Language: your system language if supported (en, zh, es, ja, hi), else English; --lang to choose.
|
|
8
9
|
// Automation/tests: --name, --canvas-host, --school, --account-id, --yes; env NCLMS_HOME, NCLMS_LANG.
|
|
10
|
+
// Exit codes: 0 done, 1 failed, 2 deployed but not confirmed online yet (run `check` later).
|
|
9
11
|
import { spawnSync } from 'node:child_process';
|
|
10
12
|
import { createRequire } from 'node:module';
|
|
11
13
|
import fs from 'node:fs';
|
|
@@ -30,11 +32,9 @@ import {
|
|
|
30
32
|
isYes,
|
|
31
33
|
needsSubdomain,
|
|
32
34
|
subdomainLink,
|
|
33
|
-
isWarmingUp,
|
|
34
|
-
CHECK_WAIT,
|
|
35
|
-
WARMUP_WAIT,
|
|
36
35
|
} from './lib.mjs';
|
|
37
36
|
import { msg } from './messages.mjs';
|
|
37
|
+
import { verifyDeployment } from './verify.mjs';
|
|
38
38
|
|
|
39
39
|
const ROOT = path.resolve(fileURLToPath(new URL('..', import.meta.url)));
|
|
40
40
|
// wrangler's package "exports" hide bin/; find the package root from its main entry instead.
|
|
@@ -230,41 +230,6 @@ async function runningVersion(url) {
|
|
|
230
230
|
}
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
-
async function healthCheck(url) {
|
|
234
|
-
let detail = 'no answer';
|
|
235
|
-
let told = false;
|
|
236
|
-
const start = Date.now();
|
|
237
|
-
for (let i = 0; Date.now() - start < (isWarmingUp(detail) ? WARMUP_WAIT : CHECK_WAIT); i++) {
|
|
238
|
-
if (i) await new Promise(r => setTimeout(r, 3000));
|
|
239
|
-
try {
|
|
240
|
-
const h = await fetch(`${url}/healthz`, {
|
|
241
|
-
headers: { 'Cache-Control': 'no-cache' },
|
|
242
|
-
signal: AbortSignal.timeout(10000),
|
|
243
|
-
});
|
|
244
|
-
const j = h.ok ? await h.json() : null;
|
|
245
|
-
if (!j?.ok) detail = `/healthz HTTP ${h.status}`;
|
|
246
|
-
else if (j.version !== VERSION) detail = `still ${j.version}`;
|
|
247
|
-
else {
|
|
248
|
-
const mcp = await fetch(`${url}/mcp`, {
|
|
249
|
-
method: 'POST',
|
|
250
|
-
body: '{}',
|
|
251
|
-
signal: AbortSignal.timeout(10000),
|
|
252
|
-
});
|
|
253
|
-
if (mcp.status === 401) return { ok: true, connected: j.connected === true };
|
|
254
|
-
detail = `/mcp HTTP ${mcp.status}`;
|
|
255
|
-
}
|
|
256
|
-
} catch (e) {
|
|
257
|
-
// fetch says only "fetch failed"; the cause names the real problem (TLS, DNS, …)
|
|
258
|
-
detail = e.cause?.code ?? e.cause?.message ?? e.message;
|
|
259
|
-
if (isWarmingUp(detail) && !told) {
|
|
260
|
-
say('cert_wait');
|
|
261
|
-
told = true;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
return { ok: false, detail };
|
|
266
|
-
}
|
|
267
|
-
|
|
268
233
|
async function install() {
|
|
269
234
|
console.log(`\n ${msg(lang, 'title')}\n`);
|
|
270
235
|
const [major] = process.versions.node.split('.').map(Number);
|
|
@@ -319,8 +284,8 @@ async function install() {
|
|
|
319
284
|
save({ url, version: VERSION });
|
|
320
285
|
|
|
321
286
|
say('check');
|
|
322
|
-
const check = await
|
|
323
|
-
if (!check.ok)
|
|
287
|
+
const check = await verifyDeployment(url, VERSION, { onWarmup: () => say('cert_wait') });
|
|
288
|
+
if (!check.ok) return notConfirmed(name, url, check.detail);
|
|
324
289
|
ok('check_ok', { version: VERSION });
|
|
325
290
|
|
|
326
291
|
// Already connected: an update needs nothing from the user, so don't send them to make a token,
|
|
@@ -333,6 +298,22 @@ async function install() {
|
|
|
333
298
|
console.log('');
|
|
334
299
|
return;
|
|
335
300
|
}
|
|
301
|
+
nextSteps(url, host);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// The deploy worked but the address did not answer correctly in time (usually a brand-new
|
|
305
|
+
// workers.dev address still getting its certificate). Not a failure: say where it is and how to
|
|
306
|
+
// check again without deploying. Exit code 2 tells scripts and agents exactly that.
|
|
307
|
+
function notConfirmed(name, url, detail) {
|
|
308
|
+
const cmd = 'npx -y neuro-canvas-lms check' + (name === DEFAULT_NAME ? '' : ` --name ${name}`);
|
|
309
|
+
console.log('');
|
|
310
|
+
say('check_pending', { url, detail, cmd });
|
|
311
|
+
console.log('');
|
|
312
|
+
process.exitCode = 2;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// What the user does next, once the connector is online and not yet connected to an AI app.
|
|
316
|
+
function nextSteps(url, host) {
|
|
336
317
|
copy(`${url}/mcp`);
|
|
337
318
|
console.log(`\n ${msg(lang, 'done')}`);
|
|
338
319
|
say('done_1', { canvas: `https://${host}` });
|
|
@@ -347,6 +328,28 @@ async function install() {
|
|
|
347
328
|
if (!args.yes) openUrl(`https://${host}/profile/settings`);
|
|
348
329
|
}
|
|
349
330
|
|
|
331
|
+
// Only check an installed connector (after "not confirmed online yet"): nothing is deployed.
|
|
332
|
+
async function check() {
|
|
333
|
+
console.log(`\n ${msg(lang, 'title')}\n`);
|
|
334
|
+
const name = args.name ?? DEFAULT_NAME;
|
|
335
|
+
if (!validWorkerName(name)) fail('invalid --name');
|
|
336
|
+
const statePath = path.join(HOME, name, 'state.json');
|
|
337
|
+
const state = fs.existsSync(statePath) ? JSON.parse(fs.readFileSync(statePath, 'utf8')) : {};
|
|
338
|
+
if (!state.url) {
|
|
339
|
+
console.error(' ✗ ' + msg(lang, 'check_none', { name }));
|
|
340
|
+
process.exitCode = 1;
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
say('check_only', { url: state.url });
|
|
344
|
+
const result = await verifyDeployment(state.url, state.version, {
|
|
345
|
+
onWarmup: () => say('cert_wait'),
|
|
346
|
+
});
|
|
347
|
+
if (!result.ok) return notConfirmed(name, state.url, result.detail);
|
|
348
|
+
ok('check_ok', { version: result.version });
|
|
349
|
+
if (result.connected) return ok('check_connected');
|
|
350
|
+
nextSteps(state.url, state.host);
|
|
351
|
+
}
|
|
352
|
+
|
|
350
353
|
async function uninstall() {
|
|
351
354
|
const name = args.name ?? DEFAULT_NAME;
|
|
352
355
|
const dir = path.join(HOME, name);
|
|
@@ -386,11 +389,11 @@ const help = async () =>
|
|
|
386
389
|
fs
|
|
387
390
|
.readFileSync(fileURLToPath(import.meta.url), 'utf8')
|
|
388
391
|
.split('\n')
|
|
389
|
-
.slice(0,
|
|
392
|
+
.slice(0, 10)
|
|
390
393
|
.join('\n'),
|
|
391
394
|
);
|
|
392
|
-
({ install, uninstall, help })[args.command]().then(
|
|
393
|
-
() => process.exit(0),
|
|
395
|
+
({ install, uninstall, help, check })[args.command]().then(
|
|
396
|
+
() => process.exit(process.exitCode ?? 0),
|
|
394
397
|
e => {
|
|
395
398
|
console.error('\n ✗ ' + msg(lang, 'failed', { detail: e?.message ?? e }) + '\n');
|
|
396
399
|
process.exit(1);
|
package/installer/lib.mjs
CHANGED
|
@@ -68,7 +68,7 @@ export function parseArgs(argv) {
|
|
|
68
68
|
const out = { command: 'install' };
|
|
69
69
|
for (let i = 0; i < argv.length; i++) {
|
|
70
70
|
const a = argv[i];
|
|
71
|
-
if (
|
|
71
|
+
if (['install', 'uninstall', 'help', 'check'].includes(a)) out.command = a;
|
|
72
72
|
else if (a === '--yes' || a === '-y') out.yes = true;
|
|
73
73
|
else if (a.startsWith('--')) {
|
|
74
74
|
const [k, v] = a.includes('=') ? a.slice(2).split(/=(.*)/s) : [a.slice(2), argv[++i]];
|
package/installer/messages.mjs
CHANGED
|
@@ -23,7 +23,6 @@ const M = {
|
|
|
23
23
|
deploy_ok: 'Connector is live: {url}',
|
|
24
24
|
check: 'Step 4/4 · Checking the connector…',
|
|
25
25
|
check_ok: 'Everything works (version {version}).',
|
|
26
|
-
check_bad: 'The connector did not pass its check: {detail}',
|
|
27
26
|
done: 'Done! Last step, in your AI:',
|
|
28
27
|
done_1:
|
|
29
28
|
'1. Create a Canvas access token: {canvas}/profile/settings → "+ New Access Token" (copy it; Canvas shows it once).',
|
|
@@ -47,6 +46,12 @@ const M = {
|
|
|
47
46
|
un_done:
|
|
48
47
|
'Removed. Also delete the Canvas token (Canvas → Account → Settings) and the connector in your AI app.',
|
|
49
48
|
cancelled: 'Cancelled.',
|
|
49
|
+
check_pending:
|
|
50
|
+
'Deployed to {url}, but it could not be confirmed online yet ({detail}). A new address can take a few more minutes the first time. Nothing needs to be deployed again: in a few minutes, check it with\n {cmd}',
|
|
51
|
+
check_none:
|
|
52
|
+
'No install named "{name}" was found (installed under another name? add --name <name>). Install it first with npx -y neuro-canvas-lms.',
|
|
53
|
+
check_only: 'Checking the connector at {url}…',
|
|
54
|
+
check_connected: 'It is connected to your AI apps and ready to use.',
|
|
50
55
|
cert_wait:
|
|
51
56
|
'The new workers.dev address is still getting its HTTPS certificate. The first time this can take a few minutes; waiting…',
|
|
52
57
|
pick_account_flag:
|
|
@@ -89,7 +94,6 @@ const M = {
|
|
|
89
94
|
deploy_ok: '连接器已上线:{url}',
|
|
90
95
|
check: '第 4/4 步 · 检查连接器…',
|
|
91
96
|
check_ok: '一切正常(版本 {version})。',
|
|
92
|
-
check_bad: '连接器没通过检查:{detail}',
|
|
93
97
|
done: '装好了!最后一步,在你的 AI 里:',
|
|
94
98
|
done_1:
|
|
95
99
|
'1. 生成 Canvas 访问令牌:{canvas}/profile/settings →「+ 新建访问许可证」(立刻复制,Canvas 只显示一次)。',
|
|
@@ -107,6 +111,12 @@ const M = {
|
|
|
107
111
|
un_failed: '没能删除{what}:{detail}\n 本地安装记录已保留,再运行一次同一条命令即可重试。',
|
|
108
112
|
un_done: '已删除。另外请删掉 Canvas 令牌(Canvas → 账户 → 设置)和 AI 应用里的连接器。',
|
|
109
113
|
cancelled: '已取消。',
|
|
114
|
+
check_pending:
|
|
115
|
+
'已部署到 {url},但还没能确认它已上线({detail})。新地址第一次可能还要几分钟。不需要重新部署:过几分钟用这条命令只做检查:\n {cmd}',
|
|
116
|
+
check_none:
|
|
117
|
+
'没找到名为「{name}」的安装(装的时候用了别的名字?加上 --name <名字>)。请先用 npx -y neuro-canvas-lms 安装。',
|
|
118
|
+
check_only: '检查连接器 {url}…',
|
|
119
|
+
check_connected: '已经连接到你的 AI 应用,可以直接用。',
|
|
110
120
|
cert_wait: '新的 workers.dev 地址还在申请 HTTPS 证书,第一次可能要几分钟,正在等待…',
|
|
111
121
|
pick_account_flag:
|
|
112
122
|
'这个 Cloudflare 登录下有多个账号。请在同一条命令后加上其中一个,再运行一次:\n{list}',
|
|
@@ -150,7 +160,6 @@ const M = {
|
|
|
150
160
|
deploy_ok: 'El conector está activo: {url}',
|
|
151
161
|
check: 'Paso 4/4 · Comprobando el conector…',
|
|
152
162
|
check_ok: 'Todo funciona (versión {version}).',
|
|
153
|
-
check_bad: 'El conector no pasó la comprobación: {detail}',
|
|
154
163
|
done: '¡Listo! Último paso, en tu IA:',
|
|
155
164
|
done_1:
|
|
156
165
|
'1. Crea un token de acceso de Canvas: {canvas}/profile/settings → "+ Nuevo token de acceso" (cópialo; Canvas lo muestra una sola vez).',
|
|
@@ -175,6 +184,12 @@ const M = {
|
|
|
175
184
|
un_done:
|
|
176
185
|
'Eliminado. Borra también el token de Canvas (Canvas → Cuenta → Configuración) y el conector en tu app de IA.',
|
|
177
186
|
cancelled: 'Cancelado.',
|
|
187
|
+
check_pending:
|
|
188
|
+
'Desplegado en {url}, pero aún no se pudo confirmar que está en línea ({detail}). Una dirección nueva puede tardar unos minutos más la primera vez. No hace falta volver a desplegar: dentro de unos minutos, compruébalo con\n {cmd}',
|
|
189
|
+
check_none:
|
|
190
|
+
'No se encontró ninguna instalación llamada "{name}" (¿la instalaste con otro nombre? añade --name <nombre>). Instálala primero con npx -y neuro-canvas-lms.',
|
|
191
|
+
check_only: 'Comprobando el conector en {url}…',
|
|
192
|
+
check_connected: 'Está conectado a tus apps de IA y listo para usar.',
|
|
178
193
|
cert_wait:
|
|
179
194
|
'La nueva dirección workers.dev todavía está obteniendo su certificado HTTPS. La primera vez puede tardar unos minutos; esperando…',
|
|
180
195
|
pick_account_flag:
|
|
@@ -220,7 +235,6 @@ const M = {
|
|
|
220
235
|
deploy_ok: 'コネクタが公開されました:{url}',
|
|
221
236
|
check: 'ステップ 4/4 · コネクタを確認しています…',
|
|
222
237
|
check_ok: 'すべて正常です(バージョン {version})。',
|
|
223
|
-
check_bad: 'コネクタが確認に失敗しました:{detail}',
|
|
224
238
|
done: '完了!最後のステップ(AI 側):',
|
|
225
239
|
done_1:
|
|
226
240
|
'1. Canvas アクセストークンを作成:{canvas}/profile/settings →「+ 新しいアクセストークン」(すぐコピー。表示は一度きりです)。',
|
|
@@ -244,6 +258,12 @@ const M = {
|
|
|
244
258
|
un_done:
|
|
245
259
|
'削除しました。Canvas トークン(Canvas → アカウント → 設定)と AI アプリのコネクタも削除してください。',
|
|
246
260
|
cancelled: 'キャンセルしました。',
|
|
261
|
+
check_pending:
|
|
262
|
+
'{url} へのデプロイは完了しましたが、まだオンラインであることを確認できていません({detail})。新しいアドレスは初回にさらに数分かかることがあります。再デプロイは不要です。数分後に次のコマンドで確認だけ行ってください:\n {cmd}',
|
|
263
|
+
check_none:
|
|
264
|
+
'「{name}」という名前のインストールが見つかりません(別の名前でインストールした場合は --name <名前> を付けてください)。先に npx -y neuro-canvas-lms でインストールしてください。',
|
|
265
|
+
check_only: '{url} のコネクタを確認しています…',
|
|
266
|
+
check_connected: 'AI アプリに接続済みで、すぐに使えます。',
|
|
247
267
|
cert_wait:
|
|
248
268
|
'新しい workers.dev アドレスの HTTPS 証明書を準備しています。初回は数分かかることがあります。待機しています…',
|
|
249
269
|
pick_account_flag:
|
|
@@ -289,7 +309,6 @@ const M = {
|
|
|
289
309
|
deploy_ok: 'कनेक्टर चालू है: {url}',
|
|
290
310
|
check: 'चरण 4/4 · कनेक्टर की जाँच हो रही है…',
|
|
291
311
|
check_ok: 'सब कुछ ठीक है (संस्करण {version})।',
|
|
292
|
-
check_bad: 'कनेक्टर जाँच में विफल रहा: {detail}',
|
|
293
312
|
done: 'हो गया! आख़िरी कदम, अपने AI में:',
|
|
294
313
|
done_1:
|
|
295
314
|
'1. Canvas एक्सेस टोकन बनाएँ: {canvas}/profile/settings → "+ New Access Token" (तुरंत कॉपी करें; Canvas इसे केवल एक बार दिखाता है)।',
|
|
@@ -313,6 +332,12 @@ const M = {
|
|
|
313
332
|
un_done:
|
|
314
333
|
'हटा दिया गया। Canvas टोकन (Canvas → Account → Settings) और अपने AI ऐप में कनेक्टर भी हटाएँ।',
|
|
315
334
|
cancelled: 'रद्द किया गया।',
|
|
335
|
+
check_pending:
|
|
336
|
+
'{url} पर डिप्लॉय हो गया, लेकिन अभी पुष्टि नहीं हो पाई कि यह ऑनलाइन है ({detail})। नए पते को पहली बार कुछ और मिनट लग सकते हैं। दोबारा डिप्लॉय करने की ज़रूरत नहीं: कुछ मिनट बाद इस कमांड से सिर्फ़ जाँच करें:\n {cmd}',
|
|
337
|
+
check_none:
|
|
338
|
+
'"{name}" नाम का कोई इंस्टॉल नहीं मिला (किसी और नाम से इंस्टॉल किया था? --name <नाम> जोड़ें)। पहले npx -y neuro-canvas-lms से इंस्टॉल करें।',
|
|
339
|
+
check_only: '{url} पर कनेक्टर की जाँच हो रही है…',
|
|
340
|
+
check_connected: 'यह आपके AI ऐप से जुड़ा है और इस्तेमाल के लिए तैयार है।',
|
|
316
341
|
cert_wait:
|
|
317
342
|
'नए workers.dev पते का HTTPS प्रमाणपत्र अभी तैयार हो रहा है। पहली बार इसमें कुछ मिनट लग सकते हैं; प्रतीक्षा हो रही है…',
|
|
318
343
|
pick_account_flag:
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Is a freshly deployed connector online and running the expected version?
|
|
2
|
+
// Ready means: /healthz answers ok with that version AND /mcp refuses an unauthenticated request
|
|
3
|
+
// with 401 (the OAuth layer is in front). Each request has its own timeout and the whole check a
|
|
4
|
+
// deadline, so it always ends. A new workers.dev address can take minutes for its DNS and HTTPS
|
|
5
|
+
// certificate: for those "not ready yet" errors the deadline is longer. Everything that touches
|
|
6
|
+
// the network or the clock can be replaced, so the behaviour is tested (verify.test.mjs).
|
|
7
|
+
import { isWarmingUp, CHECK_WAIT, WARMUP_WAIT } from './lib.mjs';
|
|
8
|
+
|
|
9
|
+
// fetch reports only "fetch failed"; the cause names the real problem (a TLS alert, DNS, …).
|
|
10
|
+
export function errorDetail(e) {
|
|
11
|
+
if (e?.name === 'TimeoutError' || e?.cause?.name === 'TimeoutError') return 'no answer (timeout)';
|
|
12
|
+
return String(e?.cause?.code ?? e?.cause?.message ?? e?.message ?? e);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function verifyDeployment(
|
|
16
|
+
url,
|
|
17
|
+
version,
|
|
18
|
+
{
|
|
19
|
+
fetcher = fetch,
|
|
20
|
+
sleep = ms => new Promise(r => setTimeout(r, ms)),
|
|
21
|
+
now = Date.now,
|
|
22
|
+
requestTimeout = 10 * 1000,
|
|
23
|
+
interval = 3 * 1000,
|
|
24
|
+
normalWait = CHECK_WAIT,
|
|
25
|
+
warmupWait = WARMUP_WAIT,
|
|
26
|
+
onWarmup = () => {},
|
|
27
|
+
} = {},
|
|
28
|
+
) {
|
|
29
|
+
let detail = 'no answer';
|
|
30
|
+
let told = false;
|
|
31
|
+
const start = now();
|
|
32
|
+
for (let i = 0; now() - start < (isWarmingUp(detail) ? warmupWait : normalWait); i++) {
|
|
33
|
+
if (i) await sleep(interval);
|
|
34
|
+
try {
|
|
35
|
+
const h = await fetcher(`${url}/healthz`, {
|
|
36
|
+
headers: { 'Cache-Control': 'no-cache' },
|
|
37
|
+
signal: AbortSignal.timeout(requestTimeout),
|
|
38
|
+
});
|
|
39
|
+
const j = h.ok ? await h.json() : null;
|
|
40
|
+
if (!j?.ok) detail = `/healthz HTTP ${h.status}`;
|
|
41
|
+
else if (version && j.version !== version) detail = `still version ${j.version}`;
|
|
42
|
+
else {
|
|
43
|
+
const mcp = await fetcher(`${url}/mcp`, {
|
|
44
|
+
method: 'POST',
|
|
45
|
+
body: '{}',
|
|
46
|
+
signal: AbortSignal.timeout(requestTimeout),
|
|
47
|
+
});
|
|
48
|
+
if (mcp.status === 401)
|
|
49
|
+
return { ok: true, version: j.version, connected: j.connected === true };
|
|
50
|
+
detail = `/mcp HTTP ${mcp.status} (expected 401)`;
|
|
51
|
+
}
|
|
52
|
+
} catch (e) {
|
|
53
|
+
detail = errorDetail(e);
|
|
54
|
+
if (isWarmingUp(detail) && !told) {
|
|
55
|
+
told = true;
|
|
56
|
+
onWarmup(detail);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return { ok: false, detail };
|
|
61
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neuro-canvas-lms",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.11.0",
|
|
4
4
|
"description": "Neuro-CanvasLMS: read-only connector that lets Claude or ChatGPT read any Canvas LMS (remote MCP server on your own Cloudflare account)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"canvas",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"repository": {
|
|
22
22
|
"type": "git",
|
|
23
|
-
"url": "https://github.com/BloodyReginaa/Neuro-CanvasLMS.git"
|
|
23
|
+
"url": "git+https://github.com/BloodyReginaa/Neuro-CanvasLMS.git"
|
|
24
24
|
},
|
|
25
25
|
"license": "MIT",
|
|
26
26
|
"author": "BloodyReginaa",
|
package/version.mjs
CHANGED