@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.
Files changed (2) hide show
  1. package/dist/server.js +119 -3
  2. 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
- this.mobileDeduper.track(session.sessionId, evt.content);
3322
- const result = await this.promptResponder.sendFreeForm(evt.content);
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, evt.content);
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.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": {