@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.
Files changed (2) hide show
  1. package/index.js +125 -94
  2. 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
- let hasError = false;
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
- hasError = true;
229
- console.error(` [1/5] Google Cloud Authentication:`);
230
- console.error(` βœ— FAILED: Not authenticated or token expired.`);
231
- console.error(` πŸ‘‰ Please run: gcloud auth login --enable-gdrive-access`);
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(` βœ“ Active: Google Drive access granted.`);
239
- console.error(` βœ“ Presentations will automatically convert into live Google Slides.`);
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
- hasError = true;
242
- console.error(` βœ— FAILED: Token lacks Google Drive access.`);
243
- console.error(` πŸ‘‰ To enable automatic Google Slides creation in your Drive, run:`);
244
- console.error(` gcloud auth login --enable-gdrive-access`);
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
- // 2. Check Project Configuration & Account Access Permissions
263
+ // 3. Check Project Configuration & Account Access Permissions
248
264
  let projectAccessOk = false;
249
265
  if (!project) {
250
- hasError = true;
251
- console.error(`\n [3/5] GEAP Billing & Quota Project:`);
252
- console.error(` βœ— FAILED: No GCP project configured.`);
253
- console.error(` πŸ‘‰ Please run: gcloud config set project YOUR_GCP_PROJECT_ID`);
254
- console.error(` πŸ‘‰ Or export: export EDITABLE_SLIDES_PROJECT=YOUR_GCP_PROJECT_ID`);
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
- console.error(
280
- ` βœ— ACCESS DENIED: Account [${account}] does NOT have permission to access project [${project}].`,
281
- );
282
- console.error(
283
- ` πŸ‘‰ Solution 1: Log in with the account that has access to [${project}]:`,
284
- );
285
- console.error(` gcloud auth login --enable-gdrive-access`);
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
- console.error(
294
- ` βœ— NOT FOUND: Project [${project}] does not exist or was deleted.`,
295
- );
296
- console.error(
297
- ` πŸ‘‰ Please run: gcloud config set project <VALID_PROJECT_ID>`,
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
- console.error(
301
- ` βœ— FAILED: Unable to verify project access (${stderr.trim()}).`,
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
- console.error(
321
- ` βœ“ GRANTED: Service account has roles/aiplatform.user on [${project}].`,
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
- hasError = true;
325
- console.error(
326
- ` βœ— MISSING: editnblm-mcp-runtime has NOT been granted roles/aiplatform.user on [${project}].`,
327
- );
328
- console.error(` πŸ‘‰ Please run the delegation commands below.`);
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
- console.error(`\n [5/5] EditNBLM Cloud Backend:`);
349
- console.error(` ⚠️ Status: HTTP ${res.status}`);
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
- console.error(`\n [5/5] EditNBLM Cloud Backend:`);
353
- console.error(` βœ— Status: Unreachable (${err.message})`);
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
- // 5. One-Time IAM Delegation command helper
357
- console.error('\n------------------------------------------------------------------------');
358
- console.error(' πŸ“‹ One-Time IAM Delegation Commands for your Project:');
359
- console.error('------------------------------------------------------------------------');
360
- const targetProject = project || 'YOUR_GCP_PROJECT_ID';
361
- console.error(`gcloud projects add-iam-policy-binding ${targetProject} \\`);
362
- console.error(' --member="serviceAccount:editnblm-mcp-runtime@editnblm-in-ge-2036.iam.gserviceaccount.com" \\');
363
- console.error(' --role="roles/aiplatform.user"\n');
364
- console.error(`gcloud projects add-iam-policy-binding ${targetProject} \\`);
365
- console.error(' --member="serviceAccount:editnblm-mcp-runtime@editnblm-in-ge-2036.iam.gserviceaccount.com" \\');
366
- console.error(' --role="roles/serviceusage.serviceUsageConsumer"');
367
- console.error('========================================================================\n');
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
- if (!hasError) {
370
- console.error('πŸŽ‰ All checks PASSED! Your environment is ready to use EditNBLM MCP.\n');
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('⚠️ Setup incomplete. Please fix the items marked with βœ— above.\n');
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('[EditNBLM] ❌ Error: Not authenticated. Run: gcloud auth login --enable-gdrive-access');
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(`[EditNBLM] ❌ Error: Google Drive scope missing. Run: gcloud auth login --enable-gdrive-access`);
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('[EditNBLM] ❌ Error: No GCP project set. Run: gcloud config set project <PROJECT_ID>');
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(`Project mismatch`);
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(`[EditNBLM] ❌ Error: Account [${account}] lacks permission for project [${project}]. Run: gcloud config set project <PROJECT_ID>`);
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(`[EditNBLM] ❌ Error: Project [${project}] not found. Run: gcloud config set project <VALID_PROJECT_ID>`);
586
+ console.error(`error: Project [${project}] not found. Run: gcloud config set project <VALID_PROJECT_ID>`);
556
587
  } else {
557
- console.error(`[EditNBLM] ❌ Error: Cannot access project [${project}] (${stderr.trim().split('\n')[0]}).`);
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(`[EditNBLM] ❌ Fatal error: ${err.message}`);
1012
+ console.error(`error: ${err.message}`);
982
1013
  process.exit(1);
983
1014
  });
984
1015
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jieunmarslim/server-editable-slides",
3
- "version": "0.2.10",
3
+ "version": "0.2.12",
4
4
  "description": "Model Context Protocol (MCP) client for Editable Slides Cloud",
5
5
  "type": "module",
6
6
  "bin": {