@jieunmarslim/server-editable-slides 0.2.11 β 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 +117 -86
- 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
|
}
|