@jieunmarslim/server-editable-slides 0.2.10 β 0.2.12
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/index.js +125 -94
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -214,47 +214,64 @@ async function runDoctor() {
|
|
|
214
214
|
const token = getAuthToken();
|
|
215
215
|
const project = getProject();
|
|
216
216
|
|
|
217
|
+
const useColor = !process.env.NO_COLOR;
|
|
218
|
+
const red = (s) => (useColor ? `\x1b[1;31m${s}\x1b[0m` : s);
|
|
219
|
+
const green = (s) => (useColor ? `\x1b[1;32m${s}\x1b[0m` : s);
|
|
220
|
+
const yellow = (s) => (useColor ? `\x1b[1;33m${s}\x1b[0m` : s);
|
|
221
|
+
const cyan = (s) => (useColor ? `\x1b[1;36m${s}\x1b[0m` : s);
|
|
222
|
+
const bold = (s) => (useColor ? `\x1b[1m${s}\x1b[0m` : s);
|
|
223
|
+
const badgePass = useColor ? '\x1b[42;97;1m PASS \x1b[0m' : '[PASS]';
|
|
224
|
+
const badgeFail = useColor ? '\x1b[41;97;1m FAILED \x1b[0m' : '[FAILED]';
|
|
225
|
+
const badgeSkip = useColor ? '\x1b[43;30;1m SKIPPED \x1b[0m' : '[SKIPPED]';
|
|
226
|
+
const badgeWarn = useColor ? '\x1b[43;30;1m WARNING \x1b[0m' : '[WARNING]';
|
|
227
|
+
|
|
217
228
|
console.error('\n========================================================================');
|
|
218
|
-
console.error(' π EditNBLM MCP Pre-flight Diagnostic & Doctor');
|
|
229
|
+
console.error(bold(' π EditNBLM MCP Pre-flight Diagnostic & Doctor'));
|
|
219
230
|
console.error('========================================================================\n');
|
|
220
231
|
|
|
221
|
-
|
|
232
|
+
const failures = [];
|
|
222
233
|
|
|
223
234
|
// 1. Check Auth
|
|
224
235
|
if (account && token) {
|
|
225
|
-
console.error(` [1/5] Google Cloud Authentication
|
|
226
|
-
console.error(` β Authenticated: ${account}`);
|
|
236
|
+
console.error(` [1/5] Google Cloud Authentication: ${badgePass}`);
|
|
237
|
+
console.error(` β Authenticated: ${green(account)}`);
|
|
227
238
|
} else {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
239
|
+
failures.push({
|
|
240
|
+
step: 'Google Cloud Authentication',
|
|
241
|
+
fix: 'gcloud auth login --enable-gdrive-access',
|
|
242
|
+
});
|
|
243
|
+
console.error(` [1/5] Google Cloud Authentication: ${badgeFail}`);
|
|
244
|
+
console.error(` ${red('β FAILED')}: Not authenticated or token expired.`);
|
|
245
|
+
console.error(` π ${bold('Required Fix')}: ${cyan('gcloud auth login --enable-gdrive-access')}`);
|
|
232
246
|
}
|
|
233
247
|
|
|
234
248
|
// 2. Check Google Drive Integration & Scope
|
|
235
249
|
const hasDrive = await checkDriveScope(token);
|
|
236
|
-
console.error(`\n [2/5] Google Drive Integration & Scope:`);
|
|
237
250
|
if (hasDrive) {
|
|
238
|
-
console.error(
|
|
239
|
-
console.error(` β
|
|
251
|
+
console.error(`\n [2/5] Google Drive Integration & Scope: ${badgePass}`);
|
|
252
|
+
console.error(` β Active: Google Drive access granted (auto Google Slides conversion ready).`);
|
|
240
253
|
} else {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
254
|
+
failures.push({
|
|
255
|
+
step: 'Google Drive Integration & Scope',
|
|
256
|
+
fix: 'gcloud auth login --enable-gdrive-access',
|
|
257
|
+
});
|
|
258
|
+
console.error(`\n [2/5] Google Drive Integration & Scope: ${badgeFail}`);
|
|
259
|
+
console.error(` ${red('β FAILED')}: Token lacks Google Drive access.`);
|
|
260
|
+
console.error(` π ${bold('Required Fix')}: ${cyan('gcloud auth login --enable-gdrive-access')}`);
|
|
245
261
|
}
|
|
246
262
|
|
|
247
|
-
//
|
|
263
|
+
// 3. Check Project Configuration & Account Access Permissions
|
|
248
264
|
let projectAccessOk = false;
|
|
249
265
|
if (!project) {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
console.error(
|
|
266
|
+
failures.push({
|
|
267
|
+
step: 'GEAP Billing & Quota Project',
|
|
268
|
+
fix: 'gcloud config set project <YOUR_GCP_PROJECT_ID>',
|
|
269
|
+
});
|
|
270
|
+
console.error(`\n [3/5] Target GCP Project Accessibility: ${badgeFail}`);
|
|
271
|
+
console.error(` ${red('β FAILED')}: No GCP project configured.`);
|
|
272
|
+
console.error(` π ${bold('Required Fix')}: ${cyan('gcloud config set project YOUR_GCP_PROJECT_ID')}`);
|
|
255
273
|
} else {
|
|
256
274
|
try {
|
|
257
|
-
// Actually verify that the logged-in account has access to this project
|
|
258
275
|
const check = execSync(
|
|
259
276
|
`gcloud projects describe ${project} --format="value(projectId)"`,
|
|
260
277
|
{ stdio: ['ignore', 'pipe', 'pipe'] },
|
|
@@ -263,48 +280,44 @@ async function runDoctor() {
|
|
|
263
280
|
.trim();
|
|
264
281
|
if (check === project) {
|
|
265
282
|
projectAccessOk = true;
|
|
266
|
-
console.error(`\n [3/5] Target GCP Project Accessibility
|
|
267
|
-
console.error(
|
|
268
|
-
` β Verified: Account [${account}] has active access to project [${project}].`,
|
|
269
|
-
);
|
|
283
|
+
console.error(`\n [3/5] Target GCP Project Accessibility: ${badgePass}`);
|
|
284
|
+
console.error(` β Verified: Account [${green(account)}] has active access to [${green(project)}].`);
|
|
270
285
|
}
|
|
271
286
|
} catch (err) {
|
|
272
|
-
hasError = true;
|
|
273
287
|
const stderr = err.stderr ? err.stderr.toString() : err.message;
|
|
274
|
-
console.error(`\n [3/5] Target GCP Project Accessibility:`);
|
|
275
288
|
if (
|
|
276
289
|
stderr.includes('does not have permission') ||
|
|
277
290
|
stderr.includes('caller does not have permission')
|
|
278
291
|
) {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
);
|
|
285
|
-
console.error(`
|
|
286
|
-
console.error(
|
|
287
|
-
` π Solution 2: Switch to a project that [${account}] can access:`,
|
|
288
|
-
);
|
|
289
|
-
console.error(
|
|
290
|
-
` gcloud config set project <ACCESSIBLE_PROJECT_ID>`,
|
|
291
|
-
);
|
|
292
|
+
failures.push({
|
|
293
|
+
step: `Access to GCP Project [${project}]`,
|
|
294
|
+
fix: `Switch project: gcloud config set project <ACCESSIBLE_PROJECT_ID>\n Or login with owner account: gcloud auth login --enable-gdrive-access`,
|
|
295
|
+
});
|
|
296
|
+
console.error(`\n [3/5] Target GCP Project Accessibility: ${badgeFail}`);
|
|
297
|
+
console.error(` ${red('β ACCESS DENIED')}: Account [${bold(account)}] does NOT have permission to access project [${bold(project)}].`);
|
|
298
|
+
console.error(` π ${bold('Solution 1 (Switch project)')}: ${cyan('gcloud config set project <ACCESSIBLE_PROJECT_ID>')}`);
|
|
299
|
+
console.error(` π ${bold('Solution 2 (Switch account)')}: ${cyan('gcloud auth login --enable-gdrive-access')}`);
|
|
292
300
|
} else if (stderr.includes('not found') || stderr.includes('404')) {
|
|
293
|
-
|
|
294
|
-
`
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
);
|
|
301
|
+
failures.push({
|
|
302
|
+
step: `GCP Project [${project}] Not Found`,
|
|
303
|
+
fix: 'gcloud config set project <VALID_PROJECT_ID>',
|
|
304
|
+
});
|
|
305
|
+
console.error(`\n [3/5] Target GCP Project Accessibility: ${badgeFail}`);
|
|
306
|
+
console.error(` ${red('β NOT FOUND')}: Project [${bold(project)}] does not exist or was deleted.`);
|
|
307
|
+
console.error(` π ${bold('Required Fix')}: ${cyan('gcloud config set project <VALID_PROJECT_ID>')}`);
|
|
299
308
|
} else {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
309
|
+
failures.push({
|
|
310
|
+
step: `Verify GCP Project [${project}]`,
|
|
311
|
+
fix: `Check gcloud permissions: ${stderr.trim().split('\n')[0]}`,
|
|
312
|
+
});
|
|
313
|
+
console.error(`\n [3/5] Target GCP Project Accessibility: ${badgeFail}`);
|
|
314
|
+
console.error(` ${red('β FAILED')}: Unable to verify project access (${stderr.trim().split('\n')[0]}).`);
|
|
303
315
|
}
|
|
304
316
|
}
|
|
305
317
|
}
|
|
306
318
|
|
|
307
319
|
// 4. Verify One-Time IAM Delegation to EditNBLM Service Account
|
|
320
|
+
let iamDelegationOk = false;
|
|
308
321
|
if (projectAccessOk) {
|
|
309
322
|
try {
|
|
310
323
|
const bindings = execSync(
|
|
@@ -315,62 +328,80 @@ async function runDoctor() {
|
|
|
315
328
|
'serviceAccount:editnblm-mcp-runtime@editnblm-in-ge-2036.iam.gserviceaccount.com',
|
|
316
329
|
);
|
|
317
330
|
|
|
318
|
-
console.error(`\n [4/5] GEAP Quota & IAM Delegation:`);
|
|
319
331
|
if (hasAiPlatform) {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
);
|
|
332
|
+
iamDelegationOk = true;
|
|
333
|
+
console.error(`\n [4/5] GEAP Quota & IAM Delegation: ${badgePass}`);
|
|
334
|
+
console.error(` β GRANTED: Service account has roles/aiplatform.user on [${green(project)}].`);
|
|
323
335
|
} else {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
);
|
|
328
|
-
console.error(
|
|
336
|
+
failures.push({
|
|
337
|
+
step: 'GEAP IAM Delegation',
|
|
338
|
+
fix: `Run the 2 IAM delegation commands shown below`,
|
|
339
|
+
});
|
|
340
|
+
console.error(`\n [4/5] GEAP Quota & IAM Delegation: ${badgeFail}`);
|
|
341
|
+
console.error(` ${red('β MISSING')}: editnblm-mcp-runtime has NOT been granted roles/aiplatform.user on [${bold(project)}].`);
|
|
342
|
+
console.error(` π ${bold('Required Action')}: Execute the IAM delegation commands below.`);
|
|
329
343
|
}
|
|
330
344
|
} catch {
|
|
331
|
-
console.error(`\n [4/5] GEAP Quota & IAM Delegation
|
|
332
|
-
console.error(
|
|
333
|
-
` β οΈ Could not read IAM policy directly. Ensure the delegation commands below were executed.`,
|
|
334
|
-
);
|
|
345
|
+
console.error(`\n [4/5] GEAP Quota & IAM Delegation: ${badgeWarn}`);
|
|
346
|
+
console.error(` β οΈ Could not verify IAM policy directly. Ensure delegation commands were executed.`);
|
|
335
347
|
}
|
|
336
348
|
} else {
|
|
337
|
-
console.error(`\n [4/5] GEAP Quota & IAM Delegation
|
|
338
|
-
console.error(` βΈοΈ Skipped until project access is resolved.`);
|
|
349
|
+
console.error(`\n [4/5] GEAP Quota & IAM Delegation: ${badgeSkip}`);
|
|
350
|
+
console.error(` βΈοΈ Skipped until project access error [3/5] is resolved.`);
|
|
339
351
|
}
|
|
340
352
|
|
|
341
353
|
// 5. Check Backend Connectivity
|
|
342
354
|
try {
|
|
343
355
|
const res = await fetch(`${BASE_URL}/health`);
|
|
344
356
|
if (res.ok) {
|
|
345
|
-
console.error(`\n [5/5] EditNBLM Cloud Backend
|
|
346
|
-
console.error(` β Status: Connected (${BASE_URL})`);
|
|
357
|
+
console.error(`\n [5/5] EditNBLM Cloud Backend: ${badgePass}`);
|
|
358
|
+
console.error(` β Status: Connected (${green(BASE_URL)})`);
|
|
347
359
|
} else {
|
|
348
|
-
|
|
349
|
-
|
|
360
|
+
failures.push({
|
|
361
|
+
step: 'EditNBLM Cloud Backend',
|
|
362
|
+
fix: `Backend returned HTTP ${res.status}. Check service health.`,
|
|
363
|
+
});
|
|
364
|
+
console.error(`\n [5/5] EditNBLM Cloud Backend: ${badgeFail}`);
|
|
365
|
+
console.error(` ${red('β HTTP ERROR')}: Status ${res.status}`);
|
|
350
366
|
}
|
|
351
367
|
} catch (err) {
|
|
352
|
-
|
|
353
|
-
|
|
368
|
+
failures.push({
|
|
369
|
+
step: 'EditNBLM Cloud Backend',
|
|
370
|
+
fix: `Backend unreachable: ${err.message}`,
|
|
371
|
+
});
|
|
372
|
+
console.error(`\n [5/5] EditNBLM Cloud Backend: ${badgeFail}`);
|
|
373
|
+
console.error(` ${red('β UNREACHABLE')}: ${err.message}`);
|
|
354
374
|
}
|
|
355
375
|
|
|
356
|
-
//
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
376
|
+
// IAM Delegation Commands (only shown when project is accessible)
|
|
377
|
+
if (projectAccessOk && !iamDelegationOk) {
|
|
378
|
+
console.error('\n------------------------------------------------------------------------');
|
|
379
|
+
console.error(bold(' π One-Time IAM Delegation Commands for your Project:'));
|
|
380
|
+
console.error('------------------------------------------------------------------------');
|
|
381
|
+
console.error(cyan(`gcloud projects add-iam-policy-binding ${project} \\`));
|
|
382
|
+
console.error(cyan(' --member="serviceAccount:editnblm-mcp-runtime@editnblm-in-ge-2036.iam.gserviceaccount.com" \\'));
|
|
383
|
+
console.error(cyan(' --role="roles/aiplatform.user"\n'));
|
|
384
|
+
console.error(cyan(`gcloud projects add-iam-policy-binding ${project} \\`));
|
|
385
|
+
console.error(cyan(' --member="serviceAccount:editnblm-mcp-runtime@editnblm-in-ge-2036.iam.gserviceaccount.com" \\'));
|
|
386
|
+
console.error(cyan(' --role="roles/serviceusage.serviceUsageConsumer"'));
|
|
387
|
+
}
|
|
368
388
|
|
|
369
|
-
|
|
370
|
-
|
|
389
|
+
// Final Summary Banner
|
|
390
|
+
if (failures.length === 0) {
|
|
391
|
+
console.error('\n' + green('========================================================================'));
|
|
392
|
+
console.error(useColor ? '\x1b[42;97;1m π ALL CHECKS PASSED! Your environment is ready to use EditNBLM MCP. \x1b[0m' : 'π ALL CHECKS PASSED! Your environment is ready to use EditNBLM MCP.');
|
|
393
|
+
console.error(green('========================================================================\n'));
|
|
371
394
|
process.exit(0);
|
|
372
395
|
} else {
|
|
373
|
-
console.error('
|
|
396
|
+
console.error('\n' + red('========================================================================'));
|
|
397
|
+
console.error(useColor ? `\x1b[41;97;1m β PRE-FLIGHT CHECK FAILED β ${failures.length} ACTION(S) REQUIRED \x1b[0m` : `β PRE-FLIGHT CHECK FAILED β ${failures.length} ACTION(S) REQUIRED`);
|
|
398
|
+
console.error(red('========================================================================'));
|
|
399
|
+
console.error(bold(`\n EditNBLM MCP cannot run until the following issue(s) are resolved:\n`));
|
|
400
|
+
failures.forEach((f, idx) => {
|
|
401
|
+
console.error(` ${red(bold(`[!] Issue ${idx + 1}`))}: ${bold(f.step)}`);
|
|
402
|
+
console.error(` π ${cyan(f.fix)}\n`);
|
|
403
|
+
});
|
|
404
|
+
console.error(red('========================================================================\n'));
|
|
374
405
|
process.exit(1);
|
|
375
406
|
}
|
|
376
407
|
}
|
|
@@ -517,19 +548,19 @@ async function assertEnvironmentOrExit() {
|
|
|
517
548
|
const project = getProject();
|
|
518
549
|
|
|
519
550
|
if (!account || !token) {
|
|
520
|
-
console.error('
|
|
551
|
+
console.error('error: Not authenticated. Please run: gcloud auth login --enable-gdrive-access');
|
|
521
552
|
process.exit(1);
|
|
522
553
|
}
|
|
523
554
|
|
|
524
555
|
// Verify Google Drive scope
|
|
525
556
|
const hasDrive = await checkDriveScope(token);
|
|
526
557
|
if (!hasDrive) {
|
|
527
|
-
console.error(
|
|
558
|
+
console.error('error: Google Drive scope missing. Please run: gcloud auth login --enable-gdrive-access');
|
|
528
559
|
process.exit(1);
|
|
529
560
|
}
|
|
530
561
|
|
|
531
562
|
if (!project) {
|
|
532
|
-
console.error('
|
|
563
|
+
console.error('error: No GCP project set. Please run: gcloud config set project <PROJECT_ID>');
|
|
533
564
|
process.exit(1);
|
|
534
565
|
}
|
|
535
566
|
|
|
@@ -542,7 +573,7 @@ async function assertEnvironmentOrExit() {
|
|
|
542
573
|
.toString()
|
|
543
574
|
.trim();
|
|
544
575
|
if (check !== project) {
|
|
545
|
-
throw new Error(
|
|
576
|
+
throw new Error('Project mismatch');
|
|
546
577
|
}
|
|
547
578
|
} catch (err) {
|
|
548
579
|
const stderr = err.stderr ? err.stderr.toString() : err.message;
|
|
@@ -550,11 +581,11 @@ async function assertEnvironmentOrExit() {
|
|
|
550
581
|
stderr.includes('does not have permission') ||
|
|
551
582
|
stderr.includes('caller does not have permission')
|
|
552
583
|
) {
|
|
553
|
-
console.error(`
|
|
584
|
+
console.error(`error: Account [${account}] lacks permission for project [${project}]. Run: gcloud config set project <PROJECT_ID>`);
|
|
554
585
|
} else if (stderr.includes('not found') || stderr.includes('404')) {
|
|
555
|
-
console.error(`
|
|
586
|
+
console.error(`error: Project [${project}] not found. Run: gcloud config set project <VALID_PROJECT_ID>`);
|
|
556
587
|
} else {
|
|
557
|
-
console.error(`
|
|
588
|
+
console.error(`error: Cannot access project [${project}] (${stderr.trim().split('\n')[0]}).`);
|
|
558
589
|
}
|
|
559
590
|
process.exit(1);
|
|
560
591
|
}
|
|
@@ -978,7 +1009,7 @@ async function startMcpServer() {
|
|
|
978
1009
|
|
|
979
1010
|
const transport = new StdioServerTransport();
|
|
980
1011
|
server.connect(transport).catch((err) => {
|
|
981
|
-
console.error(`
|
|
1012
|
+
console.error(`error: ${err.message}`);
|
|
982
1013
|
process.exit(1);
|
|
983
1014
|
});
|
|
984
1015
|
}
|