@quantiya/codevibe-antigravity-plugin 1.0.0 → 1.0.1
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/dist/server.js +119 -3
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -2826,6 +2826,7 @@ var McpServer = class {
|
|
|
2826
2826
|
this.wrapperPid = options.wrapperPid ?? null;
|
|
2827
2827
|
this.cliLogPath = options.cliLogPath ?? null;
|
|
2828
2828
|
this.appSyncClient = options.appSyncClient ?? new import_codevibe_core4.AppSyncClient();
|
|
2829
|
+
this.workingDirectory = process.cwd();
|
|
2829
2830
|
this.approvalDetector = options.approvalDetector ?? new ApprovalDetector(options.detectorOptions);
|
|
2830
2831
|
this.paneObserver = options.paneObserver ?? new TmuxPaneObserver();
|
|
2831
2832
|
this.transcriptTailer = options.transcriptTailer ?? new TranscriptTailer();
|
|
@@ -3296,6 +3297,89 @@ var McpServer = class {
|
|
|
3296
3297
|
}
|
|
3297
3298
|
}
|
|
3298
3299
|
// ─── Mobile → desktop flow ──────────────────────────────────────────────
|
|
3300
|
+
/**
|
|
3301
|
+
* Download an attachment from S3 to the working directory and return a
|
|
3302
|
+
* relative path suitable for agy's `@./path` syntax (inherited from
|
|
3303
|
+
* gemini-cli — Antigravity CLI is downstream of Gemini CLI).
|
|
3304
|
+
*
|
|
3305
|
+
* Mirrors codevibe-gemini-plugin's downloadAttachment() including the
|
|
3306
|
+
* AppSync nested-isEncrypted fallback (subscriptions return null on
|
|
3307
|
+
* nested object fields even when DynamoDB has the value).
|
|
3308
|
+
*
|
|
3309
|
+
* Returns the relative path (e.g. `./.codevibe-attachments/attachment-xxx.png`)
|
|
3310
|
+
* on success, or null on failure (logged + swallowed — one bad attachment
|
|
3311
|
+
* shouldn't block the whole prompt).
|
|
3312
|
+
*/
|
|
3313
|
+
async downloadAttachment(attachment, sessionId, sessionKey, eventIsEncrypted) {
|
|
3314
|
+
try {
|
|
3315
|
+
const shouldDecrypt = attachment.isEncrypted ?? eventIsEncrypted ?? false;
|
|
3316
|
+
logger.info("Downloading attachment", {
|
|
3317
|
+
id: attachment.id,
|
|
3318
|
+
type: attachment.type,
|
|
3319
|
+
filename: attachment.filename,
|
|
3320
|
+
s3Key: attachment.s3Key,
|
|
3321
|
+
attachmentIsEncrypted: attachment.isEncrypted,
|
|
3322
|
+
eventIsEncrypted,
|
|
3323
|
+
shouldDecrypt
|
|
3324
|
+
});
|
|
3325
|
+
const { downloadUrl } = await this.appSyncClient.getAttachmentDownloadUrl(attachment.s3Key);
|
|
3326
|
+
const response = await fetch(downloadUrl);
|
|
3327
|
+
if (!response.ok) {
|
|
3328
|
+
throw new Error(`Failed to download attachment: ${response.status} ${response.statusText}`);
|
|
3329
|
+
}
|
|
3330
|
+
let buffer = Buffer.from(await response.arrayBuffer());
|
|
3331
|
+
if (shouldDecrypt && sessionKey) {
|
|
3332
|
+
try {
|
|
3333
|
+
logger.info("Decrypting attachment", { id: attachment.id });
|
|
3334
|
+
buffer = import_codevibe_core4.cryptoService.decryptData(buffer, sessionKey);
|
|
3335
|
+
logger.info("Attachment decrypted successfully", {
|
|
3336
|
+
id: attachment.id,
|
|
3337
|
+
decryptedSize: buffer.length
|
|
3338
|
+
});
|
|
3339
|
+
} catch (decryptError) {
|
|
3340
|
+
logger.error("Failed to decrypt attachment:", { id: attachment.id, error: String(decryptError) });
|
|
3341
|
+
throw new Error("Failed to decrypt attachment");
|
|
3342
|
+
}
|
|
3343
|
+
} else if (shouldDecrypt && !sessionKey) {
|
|
3344
|
+
logger.warn("Cannot decrypt attachment - no session key available", { id: attachment.id });
|
|
3345
|
+
}
|
|
3346
|
+
const attachmentsDir = path5.join(this.workingDirectory, ".codevibe-attachments");
|
|
3347
|
+
if (!fs5.existsSync(attachmentsDir)) {
|
|
3348
|
+
fs5.mkdirSync(attachmentsDir, { recursive: true });
|
|
3349
|
+
}
|
|
3350
|
+
let extension = "";
|
|
3351
|
+
if (attachment.filename) {
|
|
3352
|
+
const ext = path5.extname(attachment.filename);
|
|
3353
|
+
if (ext) extension = ext;
|
|
3354
|
+
}
|
|
3355
|
+
if (!extension) {
|
|
3356
|
+
const mimeToExt = {
|
|
3357
|
+
"image/jpeg": ".jpg",
|
|
3358
|
+
"image/png": ".png",
|
|
3359
|
+
"image/gif": ".gif",
|
|
3360
|
+
"image/webp": ".webp",
|
|
3361
|
+
"image/heic": ".heic",
|
|
3362
|
+
"application/pdf": ".pdf"
|
|
3363
|
+
};
|
|
3364
|
+
extension = mimeToExt[attachment.type] || ".bin";
|
|
3365
|
+
}
|
|
3366
|
+
const filename = `attachment-${attachment.id}${extension}`;
|
|
3367
|
+
const absolutePath = path5.join(attachmentsDir, filename);
|
|
3368
|
+
fs5.writeFileSync(absolutePath, buffer);
|
|
3369
|
+
const relativePath = `./.codevibe-attachments/${filename}`;
|
|
3370
|
+
logger.info("Attachment saved to working directory", {
|
|
3371
|
+
id: attachment.id,
|
|
3372
|
+
sessionId,
|
|
3373
|
+
absolutePath,
|
|
3374
|
+
relativePath,
|
|
3375
|
+
size: buffer.length
|
|
3376
|
+
});
|
|
3377
|
+
return relativePath;
|
|
3378
|
+
} catch (error) {
|
|
3379
|
+
logger.error("Failed to download attachment:", { id: attachment.id, error: String(error) });
|
|
3380
|
+
return null;
|
|
3381
|
+
}
|
|
3382
|
+
}
|
|
3299
3383
|
async handleMobileEvent(evt) {
|
|
3300
3384
|
if (!this.started) return;
|
|
3301
3385
|
if (this.sessionDisabled) return;
|
|
@@ -3318,10 +3402,42 @@ var McpServer = class {
|
|
|
3318
3402
|
evt = decrypted;
|
|
3319
3403
|
await this.markDelivered(evt);
|
|
3320
3404
|
if (evt.type === import_codevibe_core4.EventType.USER_PROMPT) {
|
|
3321
|
-
|
|
3322
|
-
const
|
|
3405
|
+
let promptContent = evt.content;
|
|
3406
|
+
const attachments = evt.attachments ?? [];
|
|
3407
|
+
if (attachments.length > 0) {
|
|
3408
|
+
logger.info("Downloading attachments for USER_PROMPT", {
|
|
3409
|
+
sessionId: session.sessionId,
|
|
3410
|
+
eventId: evt.eventId,
|
|
3411
|
+
count: attachments.length
|
|
3412
|
+
});
|
|
3413
|
+
const attachmentPaths = [];
|
|
3414
|
+
for (const attachment of attachments) {
|
|
3415
|
+
const relPath = await this.downloadAttachment(
|
|
3416
|
+
attachment,
|
|
3417
|
+
session.sessionId,
|
|
3418
|
+
session.sessionKey ?? null,
|
|
3419
|
+
evt.isEncrypted
|
|
3420
|
+
);
|
|
3421
|
+
if (relPath) attachmentPaths.push(relPath);
|
|
3422
|
+
}
|
|
3423
|
+
if (attachmentPaths.length > 0) {
|
|
3424
|
+
const fileReferences = attachmentPaths.map((p) => `@${p}`).join(" ");
|
|
3425
|
+
if (promptContent) {
|
|
3426
|
+
promptContent = `${fileReferences} ${promptContent}`;
|
|
3427
|
+
} else {
|
|
3428
|
+
promptContent = `${fileReferences} Please analyze the attached file(s).`;
|
|
3429
|
+
}
|
|
3430
|
+
logger.info("Prompt augmented with attachment paths", {
|
|
3431
|
+
sessionId: session.sessionId,
|
|
3432
|
+
eventId: evt.eventId,
|
|
3433
|
+
attachmentCount: attachmentPaths.length
|
|
3434
|
+
});
|
|
3435
|
+
}
|
|
3436
|
+
}
|
|
3437
|
+
this.mobileDeduper.track(session.sessionId, promptContent);
|
|
3438
|
+
const result = await this.promptResponder.sendFreeForm(promptContent);
|
|
3323
3439
|
if (!result.ok) {
|
|
3324
|
-
this.mobileDeduper.forget(session.sessionId,
|
|
3440
|
+
this.mobileDeduper.forget(session.sessionId, promptContent);
|
|
3325
3441
|
logger.warn("sendFreeForm failed", { reason: result.reason, details: result.details });
|
|
3326
3442
|
return;
|
|
3327
3443
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quantiya/codevibe-antigravity-plugin",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Control Antigravity CLI from your iPhone and Android — real-time sync, approve file edits, send prompts by voice. Part of CodeVibe.",
|
|
5
5
|
"main": "dist/server.js",
|
|
6
6
|
"bin": {
|