UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

141 lines (135 loc) • 5.24 kB
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= import { GmailAccessor } from "../../accessor/gmail.js"; import { buildDeltaHook } from "../../core/gmail/events.js"; import { read as gmailRead } from "../../core/gmail/read.js"; import { readdir as gmailReaddir } from "../../core/gmail/readdir.js"; import { filesContaining as gmailFilesContaining } from "../../core/gmail/search.js"; import { stat as gmailStat } from "../../core/gmail/stat.js"; import { TokenManager } from "../../core/google/client.js"; import { VFSName } from "../../types.js"; import { BaseVFS } from "../base.js"; import { GmailConfig } from "./config.js"; export const VFS_MD = `\`\`\`text {prefix} <label>/ <yyyy-mm-dd>/ <subject>__<message-id>.gmail.json # the email, body included <subject>__<message-id>/ # its attachments, if any <attachment-filename> \`\`\` - \`<label>\` is a Gmail label: \`INBOX\`, \`SENT\`, \`DRAFT\`, \`IMPORTANT\`, \`STARRED\`, \`TRASH\`, \`SPAM\` or any user label. - \`<yyyy-mm-dd>\` is the date the message was received. Listing one (\`ls {prefix}/INBOX/2026-05-03/\`) pushes \`after:\`/\`before:\` into the Gmail query, much cheaper than scanning the whole label. - \`<subject>\` is sanitized: \`ls\` the date directory rather than construct it. \`<message-id>\` is the Gmail message id. - The email body is in the \`.gmail.json\` file; read it with \`cat\`, \`head\` or \`jq\`, keeping the suffix. The sibling directory, the same name without the extension, holds attachments only: \`cat\` one to download its bytes. \`grep -r\` over a date directory skips these binary files. - A \`grep\` or \`rg\` hit names the \`.gmail.json\` file, not the attachments directory. To list a message's attachments, \`ls\` its path with \`.gmail.json\` stripped; \`ENOENT\` means it has none: \`ls {prefix}/INBOX/2026-05-03/<subject>__<message-id>/\` A \`.gmail.json\` is processed by Mirage, not the raw Gmail API response: \`\`\`jsonc { "id": "...", "thread_id": "...", "from": { "name": "...", "email": "..." }, "to": [ { "name": "...", "email": "..." } ], "cc": [ ... ], "subject": "...", "date": "Mon, 3 May 2026 10:00:00 -0700", "body_text": "decoded plain-text body", "snippet": "first ~200 chars from Gmail", "labels": [ "INBOX", "IMPORTANT", ... ], "attachments": [ { "id": "...", "filename": "invoice.pdf", "mime_type": "application/pdf", "size": 12345 } ] } \`\`\` Useful \`jq\` paths: \`\`\`jq .subject .from.email .body_text .labels[] .attachments[] | .filename \`\`\` To act on messages (read by id, triage, raw API calls), use the \`gws\` CLI if installed (\`gws gmail --help\`).`; export class GmailVFS extends BaseVFS { name = VFSName.GMAIL; cachesReads = true; // Every listed file carries an exact size: .gmail.json is rendered at // readdir from the full message the listing already fetched, and // attachments carry the decoded byte count. sizesAlwaysKnown = true; indexTtl = 86_400; vfsMd = VFS_MD; // The listing keeps each message's text; an attachment is read as usual. searchable = ['*/*/*.gmail.json']; handed = { readdir: ['entry'], read: ['entry'], stat: ['entry', 'parent'], }; config; accessor; constructor(config) { super(); this.config = config; const tm = new TokenManager(config); this.accessor = new GmailAccessor({ tokenManager: tm, config, capped: () => this.walkLimit >= 0, }); } readdir(path, handed) { return gmailReaddir(this.accessor, path, handed?.entry); } async read(path, handed) { return gmailRead(this.accessor, path, handed?.entry); } deltaHook() { return buildDeltaHook(this.accessor); } stat(path, handed) { return gmailStat(this.accessor, path, handed?.entry, handed?.parent); } /** * Whether this VFS answers `name`; with no walk limit it reads every * file rather than ask the service's search. */ supports(name) { if (name === 'files_containing' && this.walkLimit < 0) return false; return super.supports(name); } filesContaining(path, text, _opts) { if (this.walkLimit < 0) return Promise.resolve(null); return gmailFilesContaining(this.accessor, text, [path]); } getState() { return Promise.resolve(this.configState(GmailConfig, this.config)); } } //# sourceMappingURL=gmail.js.map