@struktoai/mirage-node 0.0.1-alpha.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/README.md +230 -0
- package/dist/box-4P7DCJH7.js +8 -0
- package/dist/box-4P7DCJH7.js.map +1 -0
- package/dist/chunk-35PVZRWU.js +24 -0
- package/dist/chunk-35PVZRWU.js.map +1 -0
- package/dist/chunk-3PBSL7BS.js +93 -0
- package/dist/chunk-3PBSL7BS.js.map +1 -0
- package/dist/chunk-3Y625AUP.js +24 -0
- package/dist/chunk-3Y625AUP.js.map +1 -0
- package/dist/chunk-5E6BULAK.js +92 -0
- package/dist/chunk-5E6BULAK.js.map +1 -0
- package/dist/chunk-5ITBZN55.js +24 -0
- package/dist/chunk-5ITBZN55.js.map +1 -0
- package/dist/chunk-6M2NL363.js +94 -0
- package/dist/chunk-6M2NL363.js.map +1 -0
- package/dist/chunk-6M3QVVOH.js +32 -0
- package/dist/chunk-6M3QVVOH.js.map +1 -0
- package/dist/chunk-6VKNSO6U.js +56 -0
- package/dist/chunk-6VKNSO6U.js.map +1 -0
- package/dist/chunk-6Y6GWMFF.js +24 -0
- package/dist/chunk-6Y6GWMFF.js.map +1 -0
- package/dist/chunk-7QXJ4KWQ.js +132 -0
- package/dist/chunk-7QXJ4KWQ.js.map +1 -0
- package/dist/chunk-ALAMUIGJ.js +57 -0
- package/dist/chunk-ALAMUIGJ.js.map +1 -0
- package/dist/chunk-DCJQ2PIX.js +100 -0
- package/dist/chunk-DCJQ2PIX.js.map +1 -0
- package/dist/chunk-EKPH4MMD.js +27 -0
- package/dist/chunk-EKPH4MMD.js.map +1 -0
- package/dist/chunk-FZ2GJ4BJ.js +29 -0
- package/dist/chunk-FZ2GJ4BJ.js.map +1 -0
- package/dist/chunk-G7O6DAWU.js +178 -0
- package/dist/chunk-G7O6DAWU.js.map +1 -0
- package/dist/chunk-GCB4JUF7.js +94 -0
- package/dist/chunk-GCB4JUF7.js.map +1 -0
- package/dist/chunk-GKQNMLQU.js +7757 -0
- package/dist/chunk-GKQNMLQU.js.map +1 -0
- package/dist/chunk-GO67AOYK.js +24 -0
- package/dist/chunk-GO67AOYK.js.map +1 -0
- package/dist/chunk-GPF66TH2.js +24 -0
- package/dist/chunk-GPF66TH2.js.map +1 -0
- package/dist/chunk-H3JR7MN3.js +45 -0
- package/dist/chunk-H3JR7MN3.js.map +1 -0
- package/dist/chunk-H74QYTN7.js +50 -0
- package/dist/chunk-H74QYTN7.js.map +1 -0
- package/dist/chunk-HEQL3LPK.js +66 -0
- package/dist/chunk-HEQL3LPK.js.map +1 -0
- package/dist/chunk-HXWGG4K7.js +66 -0
- package/dist/chunk-HXWGG4K7.js.map +1 -0
- package/dist/chunk-I4RRGZGB.js +24 -0
- package/dist/chunk-I4RRGZGB.js.map +1 -0
- package/dist/chunk-ICSS5ETT.js +67 -0
- package/dist/chunk-ICSS5ETT.js.map +1 -0
- package/dist/chunk-KINMBZHC.js +18 -0
- package/dist/chunk-KINMBZHC.js.map +1 -0
- package/dist/chunk-LBBM3LKK.js +99 -0
- package/dist/chunk-LBBM3LKK.js.map +1 -0
- package/dist/chunk-LPMPTQYP.js +36 -0
- package/dist/chunk-LPMPTQYP.js.map +1 -0
- package/dist/chunk-M42JTWPR.js +2794 -0
- package/dist/chunk-M42JTWPR.js.map +1 -0
- package/dist/chunk-MVE5VVMQ.js +91 -0
- package/dist/chunk-MVE5VVMQ.js.map +1 -0
- package/dist/chunk-MY3NUJJD.js +67 -0
- package/dist/chunk-MY3NUJJD.js.map +1 -0
- package/dist/chunk-NOZZFJEB.js +93 -0
- package/dist/chunk-NOZZFJEB.js.map +1 -0
- package/dist/chunk-OYJXYJTE.js +2358 -0
- package/dist/chunk-OYJXYJTE.js.map +1 -0
- package/dist/chunk-PEZYBKZH.js +124 -0
- package/dist/chunk-PEZYBKZH.js.map +1 -0
- package/dist/chunk-QWWOBAYU.js +26 -0
- package/dist/chunk-QWWOBAYU.js.map +1 -0
- package/dist/chunk-RQYYYBQG.js +24 -0
- package/dist/chunk-RQYYYBQG.js.map +1 -0
- package/dist/chunk-T2WWAEKH.js +7591 -0
- package/dist/chunk-T2WWAEKH.js.map +1 -0
- package/dist/chunk-U6QSXTUS.js +7 -0
- package/dist/chunk-U6QSXTUS.js.map +1 -0
- package/dist/chunk-UWMOTOXA.js +93 -0
- package/dist/chunk-UWMOTOXA.js.map +1 -0
- package/dist/chunk-UZZD4MRJ.js +95 -0
- package/dist/chunk-UZZD4MRJ.js.map +1 -0
- package/dist/chunk-V5OLWE7F.js +88 -0
- package/dist/chunk-V5OLWE7F.js.map +1 -0
- package/dist/chunk-VCLMTMSO.js +23 -0
- package/dist/chunk-VCLMTMSO.js.map +1 -0
- package/dist/chunk-WSDQ2MRG.js +102 -0
- package/dist/chunk-WSDQ2MRG.js.map +1 -0
- package/dist/chunk-XDEEOTVK.js +93 -0
- package/dist/chunk-XDEEOTVK.js.map +1 -0
- package/dist/chunk-XQUQSNCA.js +117 -0
- package/dist/chunk-XQUQSNCA.js.map +1 -0
- package/dist/chunk-XVYZO2FG.js +14 -0
- package/dist/chunk-XVYZO2FG.js.map +1 -0
- package/dist/chunk-ZC4KNEOQ.js +50 -0
- package/dist/chunk-ZC4KNEOQ.js.map +1 -0
- package/dist/config-5ZPGISS6.js +13 -0
- package/dist/config-5ZPGISS6.js.map +1 -0
- package/dist/config-6HWSKHEU.js +9 -0
- package/dist/config-6HWSKHEU.js.map +1 -0
- package/dist/config-6TNFPYZF.js +9 -0
- package/dist/config-6TNFPYZF.js.map +1 -0
- package/dist/config-BHLXQ3OL.js +9 -0
- package/dist/config-BHLXQ3OL.js.map +1 -0
- package/dist/config-E7UOEWEP.js +9 -0
- package/dist/config-E7UOEWEP.js.map +1 -0
- package/dist/config-FNR6LJ5H.js +11 -0
- package/dist/config-FNR6LJ5H.js.map +1 -0
- package/dist/config-HIZHA5VD.js +13 -0
- package/dist/config-HIZHA5VD.js.map +1 -0
- package/dist/config-HNSDNQZH.js +9 -0
- package/dist/config-HNSDNQZH.js.map +1 -0
- package/dist/config-IXLFUSMQ.js +9 -0
- package/dist/config-IXLFUSMQ.js.map +1 -0
- package/dist/config-J3TM7NIS.js +9 -0
- package/dist/config-J3TM7NIS.js.map +1 -0
- package/dist/config-JXLZ4XEW.js +9 -0
- package/dist/config-JXLZ4XEW.js.map +1 -0
- package/dist/config-KQCEMLNZ.js +9 -0
- package/dist/config-KQCEMLNZ.js.map +1 -0
- package/dist/config-MOLWOSRG.js +9 -0
- package/dist/config-MOLWOSRG.js.map +1 -0
- package/dist/config-R3XJ5OW4.js +9 -0
- package/dist/config-R3XJ5OW4.js.map +1 -0
- package/dist/config-S47UDVRZ.js +13 -0
- package/dist/config-S47UDVRZ.js.map +1 -0
- package/dist/config-UKZ72IDO.js +13 -0
- package/dist/config-UKZ72IDO.js.map +1 -0
- package/dist/config-V7XI5UTG.js +9 -0
- package/dist/config-V7XI5UTG.js.map +1 -0
- package/dist/config-YLIX3ZBT.js +9 -0
- package/dist/config-YLIX3ZBT.js.map +1 -0
- package/dist/config-Z5JGDOKE.js +9 -0
- package/dist/config-Z5JGDOKE.js.map +1 -0
- package/dist/config-ZCZZAH5W.js +9 -0
- package/dist/config-ZCZZAH5W.js.map +1 -0
- package/dist/config-ZZGIWDUC.js +9 -0
- package/dist/config-ZZGIWDUC.js.map +1 -0
- package/dist/discord-2PWP2MGX.js +8 -0
- package/dist/discord-2PWP2MGX.js.map +1 -0
- package/dist/disk-C7TJNU2B.js +8 -0
- package/dist/disk-C7TJNU2B.js.map +1 -0
- package/dist/dropbox-L5DPYGKR.js +8 -0
- package/dist/dropbox-L5DPYGKR.js.map +1 -0
- package/dist/email-HASRFBKC.js +8 -0
- package/dist/email-HASRFBKC.js.map +1 -0
- package/dist/gcs-P5QMKTPR.js +10 -0
- package/dist/gcs-P5QMKTPR.js.map +1 -0
- package/dist/gdocs-IF2I35HZ.js +8 -0
- package/dist/gdocs-IF2I35HZ.js.map +1 -0
- package/dist/gdrive-TAETVCKP.js +8 -0
- package/dist/gdrive-TAETVCKP.js.map +1 -0
- package/dist/github-5E6GSCNZ.js +8 -0
- package/dist/github-5E6GSCNZ.js.map +1 -0
- package/dist/github_ci-2DSZI3Y7.js +8 -0
- package/dist/github_ci-2DSZI3Y7.js.map +1 -0
- package/dist/gmail-F5VYGKOT.js +8 -0
- package/dist/gmail-F5VYGKOT.js.map +1 -0
- package/dist/gsheets-BAO54CQ7.js +8 -0
- package/dist/gsheets-BAO54CQ7.js.map +1 -0
- package/dist/gslides-A62CEEMR.js +8 -0
- package/dist/gslides-A62CEEMR.js.map +1 -0
- package/dist/index.d.ts +1671 -0
- package/dist/index.js +3564 -0
- package/dist/index.js.map +1 -0
- package/dist/langfuse-YKBYRQLU.js +8 -0
- package/dist/langfuse-YKBYRQLU.js.map +1 -0
- package/dist/linear-WJDQ6JDE.js +8 -0
- package/dist/linear-WJDQ6JDE.js.map +1 -0
- package/dist/mongodb-MK4DVSIK.js +7 -0
- package/dist/mongodb-MK4DVSIK.js.map +1 -0
- package/dist/oci-KZ7XFZ2X.js +10 -0
- package/dist/oci-KZ7XFZ2X.js.map +1 -0
- package/dist/postgres-M2N2APFZ.js +7 -0
- package/dist/postgres-M2N2APFZ.js.map +1 -0
- package/dist/r2-X4DDNSAZ.js +10 -0
- package/dist/r2-X4DDNSAZ.js.map +1 -0
- package/dist/redis-NVCXUUIO.js +7 -0
- package/dist/redis-NVCXUUIO.js.map +1 -0
- package/dist/s3-HVAL7ZX3.js +8 -0
- package/dist/s3-HVAL7ZX3.js.map +1 -0
- package/dist/slack-GUQMDGLP.js +8 -0
- package/dist/slack-GUQMDGLP.js.map +1 -0
- package/dist/ssh-LY6WYVVD.js +9 -0
- package/dist/ssh-LY6WYVVD.js.map +1 -0
- package/dist/supabase-H3ZP4Y3W.js +10 -0
- package/dist/supabase-H3ZP4Y3W.js.map +1 -0
- package/dist/trello-WR6TEDYD.js +8 -0
- package/dist/trello-WR6TEDYD.js.map +1 -0
- package/package.json +118 -0
|
@@ -0,0 +1,2358 @@
|
|
|
1
|
+
import {
|
|
2
|
+
redactEmailConfig
|
|
3
|
+
} from "./chunk-H74QYTN7.js";
|
|
4
|
+
|
|
5
|
+
// src/resource/email/email.ts
|
|
6
|
+
import {
|
|
7
|
+
PathSpec as PathSpec7,
|
|
8
|
+
RAMIndexCacheStore,
|
|
9
|
+
ResourceName as ResourceName25
|
|
10
|
+
} from "@struktoai/mirage-core";
|
|
11
|
+
|
|
12
|
+
// src/accessor/email.ts
|
|
13
|
+
import { Accessor, loadOptionalPeer } from "@struktoai/mirage-core";
|
|
14
|
+
var EmailAccessor = class extends Accessor {
|
|
15
|
+
config;
|
|
16
|
+
clientPromise = null;
|
|
17
|
+
constructor(config) {
|
|
18
|
+
super();
|
|
19
|
+
this.config = config;
|
|
20
|
+
}
|
|
21
|
+
async getImap() {
|
|
22
|
+
this.clientPromise ??= (async () => {
|
|
23
|
+
const mod = await loadOptionalPeer(
|
|
24
|
+
() => import("imapflow"),
|
|
25
|
+
{ feature: "EmailAccessor", packageName: "imapflow" }
|
|
26
|
+
);
|
|
27
|
+
const client = new mod.ImapFlow({
|
|
28
|
+
host: this.config.imapHost,
|
|
29
|
+
port: this.config.imapPort,
|
|
30
|
+
secure: this.config.useSsl,
|
|
31
|
+
auth: { user: this.config.username, pass: this.config.password },
|
|
32
|
+
logger: false
|
|
33
|
+
});
|
|
34
|
+
await client.connect();
|
|
35
|
+
return client;
|
|
36
|
+
})();
|
|
37
|
+
return this.clientPromise;
|
|
38
|
+
}
|
|
39
|
+
async close() {
|
|
40
|
+
if (this.clientPromise === null) return;
|
|
41
|
+
try {
|
|
42
|
+
const c = await this.clientPromise;
|
|
43
|
+
await c.logout();
|
|
44
|
+
} catch {
|
|
45
|
+
}
|
|
46
|
+
this.clientPromise = null;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// src/commands/builtin/email/basename.ts
|
|
51
|
+
import {
|
|
52
|
+
IOResult,
|
|
53
|
+
ResourceName,
|
|
54
|
+
command,
|
|
55
|
+
specOf
|
|
56
|
+
} from "@struktoai/mirage-core";
|
|
57
|
+
var ENC = new TextEncoder();
|
|
58
|
+
function basenameOf(p) {
|
|
59
|
+
const trimmed = p.replace(/\/+$/, "");
|
|
60
|
+
const idx = trimmed.lastIndexOf("/");
|
|
61
|
+
return idx === -1 ? trimmed : trimmed.slice(idx + 1);
|
|
62
|
+
}
|
|
63
|
+
function basenameCommand(_accessor, _paths, texts, _opts) {
|
|
64
|
+
const lines = texts.map((t) => basenameOf(t));
|
|
65
|
+
const out = ENC.encode(lines.join("\n") + "\n");
|
|
66
|
+
return [out, new IOResult()];
|
|
67
|
+
}
|
|
68
|
+
var EMAIL_BASENAME = command({
|
|
69
|
+
name: "basename",
|
|
70
|
+
resource: ResourceName.EMAIL,
|
|
71
|
+
spec: specOf("basename"),
|
|
72
|
+
fn: basenameCommand
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// src/commands/builtin/email/cat.ts
|
|
76
|
+
import {
|
|
77
|
+
AsyncLineIterator,
|
|
78
|
+
IOResult as IOResult2,
|
|
79
|
+
ResourceName as ResourceName2,
|
|
80
|
+
command as command2,
|
|
81
|
+
resolveSource,
|
|
82
|
+
specOf as specOf2,
|
|
83
|
+
wrapBytes
|
|
84
|
+
} from "@struktoai/mirage-core";
|
|
85
|
+
|
|
86
|
+
// src/core/email/glob.ts
|
|
87
|
+
import { PathSpec as PathSpecCtor2 } from "@struktoai/mirage-core";
|
|
88
|
+
|
|
89
|
+
// src/core/email/readdir.ts
|
|
90
|
+
import { IndexEntry, PathSpec as PathSpecCtor } from "@struktoai/mirage-core";
|
|
91
|
+
|
|
92
|
+
// src/core/email/_parse.ts
|
|
93
|
+
import { loadOptionalPeer as loadOptionalPeer2 } from "@struktoai/mirage-core";
|
|
94
|
+
var parserPromise = null;
|
|
95
|
+
async function getParser() {
|
|
96
|
+
parserPromise ??= (async () => {
|
|
97
|
+
const mod = await loadOptionalPeer2(
|
|
98
|
+
() => import("mailparser"),
|
|
99
|
+
{ feature: "parseRfc822", packageName: "mailparser" }
|
|
100
|
+
);
|
|
101
|
+
return mod.simpleParser;
|
|
102
|
+
})();
|
|
103
|
+
return parserPromise;
|
|
104
|
+
}
|
|
105
|
+
function toAddr(a) {
|
|
106
|
+
if (a === void 0) return { name: "", email: "" };
|
|
107
|
+
return { name: a.name, email: a.address ?? "" };
|
|
108
|
+
}
|
|
109
|
+
function toAddrList(list) {
|
|
110
|
+
if (list === void 0) return [];
|
|
111
|
+
const objs = Array.isArray(list) ? list : [list];
|
|
112
|
+
return objs.flatMap((obj) => obj.value.map((a) => toAddr(a)));
|
|
113
|
+
}
|
|
114
|
+
function fromParsed(parsed, headersOnly) {
|
|
115
|
+
const text = headersOnly ? "" : parsed.text ?? "";
|
|
116
|
+
const html = headersOnly ? "" : typeof parsed.html === "string" ? parsed.html : "";
|
|
117
|
+
const attachments = headersOnly ? [] : parsed.attachments.map((a) => ({
|
|
118
|
+
filename: a.filename ?? "unnamed",
|
|
119
|
+
content_type: a.contentType,
|
|
120
|
+
size: a.size
|
|
121
|
+
}));
|
|
122
|
+
const refsRaw = parsed.references;
|
|
123
|
+
const references = typeof refsRaw === "string" ? refsRaw.split(/\s+/).filter((s) => s !== "") : refsRaw ?? [];
|
|
124
|
+
return {
|
|
125
|
+
from: toAddr(parsed.from?.value[0]),
|
|
126
|
+
to: toAddrList(parsed.to),
|
|
127
|
+
cc: toAddrList(parsed.cc),
|
|
128
|
+
subject: parsed.subject ?? "",
|
|
129
|
+
date: parsed.date instanceof Date ? parsed.date.toUTCString() : "",
|
|
130
|
+
body_text: text,
|
|
131
|
+
body_html: html,
|
|
132
|
+
snippet: text.slice(0, 100),
|
|
133
|
+
message_id: parsed.messageId ?? "",
|
|
134
|
+
in_reply_to: typeof parsed.inReplyTo === "string" && parsed.inReplyTo !== "" ? parsed.inReplyTo : null,
|
|
135
|
+
references,
|
|
136
|
+
has_attachments: attachments.length > 0,
|
|
137
|
+
attachments
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
async function parseRfc822(raw, headersOnly = false) {
|
|
141
|
+
const parser = await getParser();
|
|
142
|
+
const parsed = await parser(Buffer.from(raw), { skipHtmlToText: false });
|
|
143
|
+
return fromParsed(parsed, headersOnly);
|
|
144
|
+
}
|
|
145
|
+
async function parseWithPayloads(raw) {
|
|
146
|
+
const parser = await getParser();
|
|
147
|
+
const parsed = await parser(Buffer.from(raw));
|
|
148
|
+
return parsed.attachments.map((a) => ({
|
|
149
|
+
filename: a.filename ?? "unnamed",
|
|
150
|
+
content_type: a.contentType,
|
|
151
|
+
size: a.size,
|
|
152
|
+
payload: new Uint8Array(a.content)
|
|
153
|
+
}));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/core/email/_client.ts
|
|
157
|
+
async function listFolders(accessor) {
|
|
158
|
+
const imap = await accessor.getImap();
|
|
159
|
+
const tree = await imap.list();
|
|
160
|
+
return tree.map((m) => m.path);
|
|
161
|
+
}
|
|
162
|
+
async function listMessageUids(accessor, folder, searchCriteria = "ALL", maxResults = null) {
|
|
163
|
+
const imap = await accessor.getImap();
|
|
164
|
+
const lock = await imap.getMailboxLock(folder);
|
|
165
|
+
try {
|
|
166
|
+
const query = parseSearchCriteria(searchCriteria);
|
|
167
|
+
const uidsRaw = await imap.search(query, { uid: true });
|
|
168
|
+
if (uidsRaw === false) return [];
|
|
169
|
+
const uids = uidsRaw.map((n) => String(n));
|
|
170
|
+
if (maxResults !== null && uids.length > maxResults) {
|
|
171
|
+
return uids.slice(uids.length - maxResults);
|
|
172
|
+
}
|
|
173
|
+
return uids;
|
|
174
|
+
} finally {
|
|
175
|
+
lock.release();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function parseSearchCriteria(criteria) {
|
|
179
|
+
if (criteria === "ALL" || criteria === "") return { all: true };
|
|
180
|
+
const out = {};
|
|
181
|
+
const tokens = tokenizeCriteria(criteria);
|
|
182
|
+
let i = 0;
|
|
183
|
+
while (i < tokens.length) {
|
|
184
|
+
const tok = (tokens[i] ?? "").toUpperCase();
|
|
185
|
+
if (tok === "UNSEEN") {
|
|
186
|
+
out.unseen = true;
|
|
187
|
+
i += 1;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (tok === "TEXT" || tok === "SUBJECT" || tok === "FROM" || tok === "TO" || tok === "SINCE" || tok === "BEFORE") {
|
|
191
|
+
const v = tokens[i + 1] ?? "";
|
|
192
|
+
if (tok === "TEXT") out.text = v;
|
|
193
|
+
else if (tok === "SUBJECT") out.subject = v;
|
|
194
|
+
else if (tok === "FROM") out.from = v;
|
|
195
|
+
else if (tok === "TO") out.to = v;
|
|
196
|
+
else if (tok === "SINCE") {
|
|
197
|
+
const d = parseImapDate(v);
|
|
198
|
+
if (d !== null) out.since = d;
|
|
199
|
+
} else {
|
|
200
|
+
const d = parseImapDate(v);
|
|
201
|
+
if (d !== null) out.before = d;
|
|
202
|
+
}
|
|
203
|
+
i += 2;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
i += 1;
|
|
207
|
+
}
|
|
208
|
+
return out;
|
|
209
|
+
}
|
|
210
|
+
function tokenizeCriteria(criteria) {
|
|
211
|
+
const tokens = [];
|
|
212
|
+
let i = 0;
|
|
213
|
+
while (i < criteria.length) {
|
|
214
|
+
if (criteria[i] === " ") {
|
|
215
|
+
i += 1;
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (criteria[i] === '"') {
|
|
219
|
+
const end2 = criteria.indexOf('"', i + 1);
|
|
220
|
+
if (end2 === -1) {
|
|
221
|
+
tokens.push(criteria.slice(i + 1));
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
tokens.push(criteria.slice(i + 1, end2));
|
|
225
|
+
i = end2 + 1;
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
let end = criteria.indexOf(" ", i);
|
|
229
|
+
if (end === -1) end = criteria.length;
|
|
230
|
+
tokens.push(criteria.slice(i, end));
|
|
231
|
+
i = end;
|
|
232
|
+
}
|
|
233
|
+
return tokens;
|
|
234
|
+
}
|
|
235
|
+
function parseImapDate(s) {
|
|
236
|
+
const m = /^(\d{1,2})-([A-Za-z]{3})-(\d{4})$/.exec(s);
|
|
237
|
+
if (m === null) return null;
|
|
238
|
+
const months = {
|
|
239
|
+
Jan: 0,
|
|
240
|
+
Feb: 1,
|
|
241
|
+
Mar: 2,
|
|
242
|
+
Apr: 3,
|
|
243
|
+
May: 4,
|
|
244
|
+
Jun: 5,
|
|
245
|
+
Jul: 6,
|
|
246
|
+
Aug: 7,
|
|
247
|
+
Sep: 8,
|
|
248
|
+
Oct: 9,
|
|
249
|
+
Nov: 10,
|
|
250
|
+
Dec: 11
|
|
251
|
+
};
|
|
252
|
+
const day = Number.parseInt(m[1] ?? "0", 10);
|
|
253
|
+
const monKey = (m[2] ?? "").slice(0, 1).toUpperCase() + (m[2] ?? "").slice(1).toLowerCase();
|
|
254
|
+
const mon = months[monKey] ?? 0;
|
|
255
|
+
const year = Number.parseInt(m[3] ?? "1970", 10);
|
|
256
|
+
return new Date(Date.UTC(year, mon, day));
|
|
257
|
+
}
|
|
258
|
+
async function fetchMessage(accessor, folder, uid) {
|
|
259
|
+
const imap = await accessor.getImap();
|
|
260
|
+
const lock = await imap.getMailboxLock(folder);
|
|
261
|
+
try {
|
|
262
|
+
const msg = await imap.fetchOne(uid, { source: true, flags: true, uid: true }, { uid: true });
|
|
263
|
+
if (msg === false) {
|
|
264
|
+
throw new Error(`email: uid ${uid} not found in ${folder}`);
|
|
265
|
+
}
|
|
266
|
+
const source = msg.source instanceof Buffer ? new Uint8Array(msg.source) : new Uint8Array(0);
|
|
267
|
+
const parsed = await parseRfc822(source);
|
|
268
|
+
return {
|
|
269
|
+
...parsed,
|
|
270
|
+
uid,
|
|
271
|
+
flags: msg.flags !== void 0 ? [...msg.flags] : []
|
|
272
|
+
};
|
|
273
|
+
} finally {
|
|
274
|
+
lock.release();
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
async function fetchHeaders(accessor, folder, uids) {
|
|
278
|
+
if (uids.length === 0) return [];
|
|
279
|
+
const imap = await accessor.getImap();
|
|
280
|
+
const lock = await imap.getMailboxLock(folder);
|
|
281
|
+
try {
|
|
282
|
+
const results = [];
|
|
283
|
+
for (const uid of uids) {
|
|
284
|
+
const msg = await imap.fetchOne(uid, { headers: true, flags: true, uid: true }, { uid: true });
|
|
285
|
+
if (msg === false) continue;
|
|
286
|
+
const headers = msg.headers instanceof Buffer ? new Uint8Array(msg.headers) : new Uint8Array(0);
|
|
287
|
+
const parsed = await parseRfc822(headers, true);
|
|
288
|
+
results.push({
|
|
289
|
+
...parsed,
|
|
290
|
+
uid,
|
|
291
|
+
flags: msg.flags !== void 0 ? [...msg.flags] : []
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
return results;
|
|
295
|
+
} finally {
|
|
296
|
+
lock.release();
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
async function fetchAttachment(accessor, folder, uid, filename) {
|
|
300
|
+
const imap = await accessor.getImap();
|
|
301
|
+
const lock = await imap.getMailboxLock(folder);
|
|
302
|
+
try {
|
|
303
|
+
const msg = await imap.fetchOne(uid, { source: true }, { uid: true });
|
|
304
|
+
if (msg === false) return null;
|
|
305
|
+
const source = msg.source instanceof Buffer ? new Uint8Array(msg.source) : new Uint8Array(0);
|
|
306
|
+
const attachments = await parseWithPayloads(source);
|
|
307
|
+
for (const att of attachments) {
|
|
308
|
+
if (att.filename === filename) return att.payload;
|
|
309
|
+
}
|
|
310
|
+
return null;
|
|
311
|
+
} finally {
|
|
312
|
+
lock.release();
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// src/core/email/folders.ts
|
|
317
|
+
async function listFolders2(accessor) {
|
|
318
|
+
return listFolders(accessor);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// src/core/email/readdir.ts
|
|
322
|
+
var TITLE_MAX = 80;
|
|
323
|
+
var UNSAFE = /[^\w\s\-.]/g;
|
|
324
|
+
var MULTI_UNDERSCORE = /_+/g;
|
|
325
|
+
function sanitize(text) {
|
|
326
|
+
if (text.trim() === "") return "No_Subject";
|
|
327
|
+
let cleaned = text.replace(UNSAFE, "_").replace(/ /g, "_");
|
|
328
|
+
cleaned = cleaned.replace(MULTI_UNDERSCORE, "_").replace(/^_+|_+$/g, "");
|
|
329
|
+
if (cleaned.length > TITLE_MAX) cleaned = `${cleaned.slice(0, TITLE_MAX - 3)}...`;
|
|
330
|
+
return cleaned;
|
|
331
|
+
}
|
|
332
|
+
function msgFilename(subject, uid) {
|
|
333
|
+
return `${sanitize(subject)}__${uid}.email.json`;
|
|
334
|
+
}
|
|
335
|
+
function dateFromHeader(dateStr) {
|
|
336
|
+
if (dateStr === "") return "1970-01-01";
|
|
337
|
+
const d = new Date(dateStr);
|
|
338
|
+
if (Number.isNaN(d.getTime())) return "1970-01-01";
|
|
339
|
+
const yyyy = d.getUTCFullYear().toString().padStart(4, "0");
|
|
340
|
+
const mm = (d.getUTCMonth() + 1).toString().padStart(2, "0");
|
|
341
|
+
const dd = d.getUTCDate().toString().padStart(2, "0");
|
|
342
|
+
return `${yyyy}-${mm}-${dd}`;
|
|
343
|
+
}
|
|
344
|
+
function enoent(p) {
|
|
345
|
+
const e = new Error(`ENOENT: ${p}`);
|
|
346
|
+
e.code = "ENOENT";
|
|
347
|
+
return e;
|
|
348
|
+
}
|
|
349
|
+
async function readdir(accessor, path, index) {
|
|
350
|
+
const prefix = path.prefix;
|
|
351
|
+
const raw = path.pattern !== null ? path.directory : path.original;
|
|
352
|
+
let p = raw;
|
|
353
|
+
if (prefix !== "" && p.startsWith(prefix)) p = p.slice(prefix.length) || "/";
|
|
354
|
+
const key = p.replace(/^\/+|\/+$/g, "");
|
|
355
|
+
const virtualKey = key !== "" ? `${prefix}/${key}` : prefix !== "" ? prefix : "/";
|
|
356
|
+
const parts = key === "" ? [] : key.split("/");
|
|
357
|
+
const depth = parts.length;
|
|
358
|
+
if (depth === 0) {
|
|
359
|
+
if (index !== void 0) {
|
|
360
|
+
const cached = await index.listDir(virtualKey);
|
|
361
|
+
if (cached.entries !== void 0 && cached.entries !== null) return cached.entries;
|
|
362
|
+
}
|
|
363
|
+
const folders = await listFolders2(accessor);
|
|
364
|
+
const entries = [];
|
|
365
|
+
for (const folderName of folders) {
|
|
366
|
+
const entry = new IndexEntry({
|
|
367
|
+
id: folderName,
|
|
368
|
+
name: folderName,
|
|
369
|
+
resourceType: "email/folder",
|
|
370
|
+
vfsName: folderName
|
|
371
|
+
});
|
|
372
|
+
entries.push([folderName, entry]);
|
|
373
|
+
}
|
|
374
|
+
if (index !== void 0) await index.setDir(virtualKey, entries);
|
|
375
|
+
return entries.map(([name]) => `${prefix}/${name}`);
|
|
376
|
+
}
|
|
377
|
+
if (depth === 1) {
|
|
378
|
+
const folderName = parts[0] ?? "";
|
|
379
|
+
if (index !== void 0) {
|
|
380
|
+
const cached = await index.listDir(virtualKey);
|
|
381
|
+
if (cached.entries !== void 0 && cached.entries !== null) return cached.entries;
|
|
382
|
+
}
|
|
383
|
+
if (index === void 0) throw enoent(path.original);
|
|
384
|
+
const maxMessages = accessor.config.maxMessages;
|
|
385
|
+
const uids = await listMessageUids(accessor, folderName, "ALL", maxMessages);
|
|
386
|
+
const headersList = await fetchHeaders(accessor, folderName, uids);
|
|
387
|
+
const dateGroups = /* @__PURE__ */ new Map();
|
|
388
|
+
for (const hdr of headersList) {
|
|
389
|
+
const dateStr = dateFromHeader(hdr.date);
|
|
390
|
+
let bucket = dateGroups.get(dateStr);
|
|
391
|
+
if (bucket === void 0) {
|
|
392
|
+
bucket = [];
|
|
393
|
+
dateGroups.set(dateStr, bucket);
|
|
394
|
+
}
|
|
395
|
+
bucket.push(hdr);
|
|
396
|
+
}
|
|
397
|
+
const sortedDates = [...dateGroups.keys()].sort().reverse();
|
|
398
|
+
const dateEntries = [];
|
|
399
|
+
for (const dateStr of sortedDates) {
|
|
400
|
+
const dateEntry = new IndexEntry({
|
|
401
|
+
id: dateStr,
|
|
402
|
+
name: dateStr,
|
|
403
|
+
resourceType: "email/date",
|
|
404
|
+
vfsName: dateStr
|
|
405
|
+
});
|
|
406
|
+
dateEntries.push([dateStr, dateEntry]);
|
|
407
|
+
const msgEntries = [];
|
|
408
|
+
for (const hdr of dateGroups.get(dateStr) ?? []) {
|
|
409
|
+
const uid = hdr.uid;
|
|
410
|
+
const subject = hdr.subject || "No Subject";
|
|
411
|
+
const filename = msgFilename(subject, uid);
|
|
412
|
+
const msgEntry = new IndexEntry({
|
|
413
|
+
id: uid,
|
|
414
|
+
name: subject,
|
|
415
|
+
resourceType: "email/message",
|
|
416
|
+
vfsName: filename
|
|
417
|
+
});
|
|
418
|
+
msgEntries.push([filename, msgEntry]);
|
|
419
|
+
const attachments = hdr.attachments;
|
|
420
|
+
if (attachments.length > 0) {
|
|
421
|
+
const attDirName = filename.replace(".email.json", "");
|
|
422
|
+
const attDirEntry = new IndexEntry({
|
|
423
|
+
id: uid,
|
|
424
|
+
name: attDirName,
|
|
425
|
+
resourceType: "email/attachment_dir",
|
|
426
|
+
vfsName: attDirName
|
|
427
|
+
});
|
|
428
|
+
msgEntries.push([attDirName, attDirEntry]);
|
|
429
|
+
const attEntries = [];
|
|
430
|
+
for (const att of attachments) {
|
|
431
|
+
const attEntry = new IndexEntry({
|
|
432
|
+
id: att.filename,
|
|
433
|
+
name: att.filename,
|
|
434
|
+
resourceType: "email/attachment",
|
|
435
|
+
vfsName: att.filename,
|
|
436
|
+
size: att.size
|
|
437
|
+
});
|
|
438
|
+
attEntries.push([att.filename, attEntry]);
|
|
439
|
+
}
|
|
440
|
+
const attDirVKey = `${virtualKey}/${dateStr}/${attDirName}`;
|
|
441
|
+
await index.setDir(attDirVKey, attEntries);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
const dateVKey = `${virtualKey}/${dateStr}`;
|
|
445
|
+
await index.setDir(dateVKey, msgEntries);
|
|
446
|
+
}
|
|
447
|
+
await index.setDir(virtualKey, dateEntries);
|
|
448
|
+
return dateEntries.map(([name]) => `${prefix}/${key}/${name}`);
|
|
449
|
+
}
|
|
450
|
+
if (depth === 2 || depth === 3) {
|
|
451
|
+
if (index === void 0) throw enoent(path.original);
|
|
452
|
+
let cached = await index.listDir(virtualKey);
|
|
453
|
+
if (cached.entries !== void 0 && cached.entries !== null) return cached.entries;
|
|
454
|
+
const folderPath = prefix !== "" ? `${prefix}/${parts[0] ?? ""}` : `/${parts[0] ?? ""}`;
|
|
455
|
+
const folderSpec = new PathSpecCtor({ original: folderPath, directory: folderPath, prefix });
|
|
456
|
+
await readdir(accessor, folderSpec, index);
|
|
457
|
+
cached = await index.listDir(virtualKey);
|
|
458
|
+
if (cached.entries !== void 0 && cached.entries !== null) return cached.entries;
|
|
459
|
+
throw enoent(path.original);
|
|
460
|
+
}
|
|
461
|
+
throw enoent(path.original);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// src/core/email/glob.ts
|
|
465
|
+
var SCOPE_ERROR = 1e3;
|
|
466
|
+
function fnmatch(name, pattern) {
|
|
467
|
+
const re = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\?/g, ".").replace(/\*/g, ".*");
|
|
468
|
+
return new RegExp(`^${re}$`).test(name);
|
|
469
|
+
}
|
|
470
|
+
async function resolveGlob(accessor, paths, index) {
|
|
471
|
+
const result = [];
|
|
472
|
+
for (const p of paths) {
|
|
473
|
+
if (p.resolved) {
|
|
474
|
+
result.push(p);
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
if (p.pattern !== null && p.pattern !== "") {
|
|
478
|
+
const entries = await readdir(accessor, p, index);
|
|
479
|
+
const matched = [];
|
|
480
|
+
for (const entry of entries) {
|
|
481
|
+
const trimmed = entry.replace(/\/+$/, "");
|
|
482
|
+
const base = trimmed.split("/").pop() ?? trimmed;
|
|
483
|
+
if (!fnmatch(base, p.pattern)) continue;
|
|
484
|
+
matched.push(PathSpecCtor2.fromStrPath(trimmed, p.prefix));
|
|
485
|
+
}
|
|
486
|
+
const truncated = matched.length > SCOPE_ERROR ? matched.slice(0, SCOPE_ERROR) : matched;
|
|
487
|
+
result.push(...truncated);
|
|
488
|
+
} else {
|
|
489
|
+
result.push(p);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return result;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// src/core/email/read.ts
|
|
496
|
+
var ENC2 = new TextEncoder();
|
|
497
|
+
function enoent2(p) {
|
|
498
|
+
const e = new Error(`ENOENT: ${p}`);
|
|
499
|
+
e.code = "ENOENT";
|
|
500
|
+
return e;
|
|
501
|
+
}
|
|
502
|
+
function eisdir(p) {
|
|
503
|
+
const e = new Error(`EISDIR: ${p}`);
|
|
504
|
+
e.code = "EISDIR";
|
|
505
|
+
return e;
|
|
506
|
+
}
|
|
507
|
+
function dirname(p) {
|
|
508
|
+
const norm = p.replace(/\/+$/, "");
|
|
509
|
+
const idx = norm.lastIndexOf("/");
|
|
510
|
+
if (idx <= 0) return "/";
|
|
511
|
+
return norm.slice(0, idx);
|
|
512
|
+
}
|
|
513
|
+
async function read(accessor, path, index) {
|
|
514
|
+
const prefix = path.prefix;
|
|
515
|
+
let p = path.original;
|
|
516
|
+
if (prefix !== "" && p.startsWith(prefix)) p = p.slice(prefix.length) || "/";
|
|
517
|
+
const key = p.replace(/^\/+|\/+$/g, "");
|
|
518
|
+
if (index === void 0) throw enoent2(path.original);
|
|
519
|
+
const virtualKey = prefix !== "" ? `${prefix}/${key}` : `/${key}`;
|
|
520
|
+
const result = await index.get(virtualKey);
|
|
521
|
+
if (result.entry === void 0 || result.entry === null) throw enoent2(path.original);
|
|
522
|
+
const rt = result.entry.resourceType;
|
|
523
|
+
if (rt === "email/folder" || rt === "email/date" || rt === "email/attachment_dir") {
|
|
524
|
+
throw eisdir(path.original);
|
|
525
|
+
}
|
|
526
|
+
if (rt === "email/attachment") {
|
|
527
|
+
const parentKey = dirname(virtualKey);
|
|
528
|
+
const parentResult = await index.get(parentKey);
|
|
529
|
+
if (parentResult.entry === void 0 || parentResult.entry === null) {
|
|
530
|
+
throw enoent2(path.original);
|
|
531
|
+
}
|
|
532
|
+
const uid2 = parentResult.entry.id;
|
|
533
|
+
const parts2 = virtualKey.replace(/^\/+|\/+$/g, "").split("/");
|
|
534
|
+
const folder2 = prefix !== "" ? parts2[1] ?? "" : parts2[0] ?? "";
|
|
535
|
+
const filename = result.entry.vfsName !== "" ? result.entry.vfsName : result.entry.name;
|
|
536
|
+
const data = await fetchAttachment(accessor, folder2, uid2, filename);
|
|
537
|
+
if (data === null) throw enoent2(path.original);
|
|
538
|
+
return data;
|
|
539
|
+
}
|
|
540
|
+
const parts = virtualKey.replace(/^\/+|\/+$/g, "").split("/");
|
|
541
|
+
const folder = prefix !== "" ? parts[1] ?? "" : parts[0] ?? "";
|
|
542
|
+
const uid = result.entry.id;
|
|
543
|
+
const msg = await fetchMessage(accessor, folder, uid);
|
|
544
|
+
return ENC2.encode(JSON.stringify(msg));
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// src/commands/builtin/email/provision.ts
|
|
548
|
+
import {
|
|
549
|
+
Precision,
|
|
550
|
+
ProvisionResult
|
|
551
|
+
} from "@struktoai/mirage-core";
|
|
552
|
+
async function fileReadProvision(_accessor, paths, _texts, opts) {
|
|
553
|
+
if (paths.length === 0) return new ProvisionResult({ precision: Precision.UNKNOWN });
|
|
554
|
+
const index = opts.index ?? void 0;
|
|
555
|
+
let ops = 0;
|
|
556
|
+
if (index !== void 0) {
|
|
557
|
+
for (const p of paths) {
|
|
558
|
+
const lookup = await index.get(p.original);
|
|
559
|
+
if (lookup.entry !== void 0 && lookup.entry !== null) ops += 1;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
return new ProvisionResult({
|
|
563
|
+
networkReadLow: 0,
|
|
564
|
+
networkReadHigh: 0,
|
|
565
|
+
readOps: ops,
|
|
566
|
+
precision: Precision.EXACT
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
function metadataProvision(_accessor, _paths, _texts, _opts) {
|
|
570
|
+
return new ProvisionResult({
|
|
571
|
+
networkReadLow: 0,
|
|
572
|
+
networkReadHigh: 0,
|
|
573
|
+
readOps: 0,
|
|
574
|
+
precision: Precision.EXACT
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// src/commands/builtin/email/cat.ts
|
|
579
|
+
var ENC3 = new TextEncoder();
|
|
580
|
+
async function* numberLinesStream(source) {
|
|
581
|
+
let num = 1;
|
|
582
|
+
const lineIter = new AsyncLineIterator(source);
|
|
583
|
+
for await (const line of lineIter) {
|
|
584
|
+
yield ENC3.encode(` ${String(num)} `);
|
|
585
|
+
yield line;
|
|
586
|
+
yield ENC3.encode("\n");
|
|
587
|
+
num += 1;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
async function catCommand(accessor, paths, _texts, opts) {
|
|
591
|
+
const nFlag = opts.flags.n === true;
|
|
592
|
+
if (paths.length > 0) {
|
|
593
|
+
const resolved = await resolveGlob(accessor, paths, opts.index ?? void 0);
|
|
594
|
+
const first = resolved[0];
|
|
595
|
+
if (first === void 0) return [null, new IOResult2()];
|
|
596
|
+
const data = await read(accessor, first, opts.index ?? void 0);
|
|
597
|
+
const io = new IOResult2({ reads: { [first.stripPrefix]: data }, cache: [first.stripPrefix] });
|
|
598
|
+
if (nFlag) return [numberLinesStream(wrapBytes(data)), io];
|
|
599
|
+
return [data, io];
|
|
600
|
+
}
|
|
601
|
+
try {
|
|
602
|
+
const source = resolveSource(opts.stdin, "cat: missing operand");
|
|
603
|
+
if (nFlag) return [numberLinesStream(source), new IOResult2()];
|
|
604
|
+
return [source, new IOResult2()];
|
|
605
|
+
} catch (err) {
|
|
606
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
607
|
+
return [null, new IOResult2({ exitCode: 1, stderr: ENC3.encode(`${msg}
|
|
608
|
+
`) })];
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
var EMAIL_CAT = command2({
|
|
612
|
+
name: "cat",
|
|
613
|
+
resource: ResourceName2.EMAIL,
|
|
614
|
+
spec: specOf2("cat"),
|
|
615
|
+
fn: catCommand,
|
|
616
|
+
provision: fileReadProvision
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
// src/commands/builtin/email/dirname.ts
|
|
620
|
+
import {
|
|
621
|
+
IOResult as IOResult3,
|
|
622
|
+
ResourceName as ResourceName3,
|
|
623
|
+
command as command3,
|
|
624
|
+
specOf as specOf3
|
|
625
|
+
} from "@struktoai/mirage-core";
|
|
626
|
+
var ENC4 = new TextEncoder();
|
|
627
|
+
function dirnameOf(p) {
|
|
628
|
+
const trimmed = p.replace(/\/+$/, "");
|
|
629
|
+
const idx = trimmed.lastIndexOf("/");
|
|
630
|
+
if (idx === -1) return ".";
|
|
631
|
+
if (idx === 0) return "/";
|
|
632
|
+
return trimmed.slice(0, idx);
|
|
633
|
+
}
|
|
634
|
+
function dirnameCommand(_accessor, _paths, texts, _opts) {
|
|
635
|
+
const lines = texts.map((t) => dirnameOf(t));
|
|
636
|
+
const out = ENC4.encode(lines.join("\n") + "\n");
|
|
637
|
+
return [out, new IOResult3()];
|
|
638
|
+
}
|
|
639
|
+
var EMAIL_DIRNAME = command3({
|
|
640
|
+
name: "dirname",
|
|
641
|
+
resource: ResourceName3.EMAIL,
|
|
642
|
+
spec: specOf3("dirname"),
|
|
643
|
+
fn: dirnameCommand
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
// src/commands/builtin/email/find.ts
|
|
647
|
+
import {
|
|
648
|
+
IOResult as IOResult4,
|
|
649
|
+
PathSpec,
|
|
650
|
+
ResourceName as ResourceName4,
|
|
651
|
+
command as command4,
|
|
652
|
+
specOf as specOf4
|
|
653
|
+
} from "@struktoai/mirage-core";
|
|
654
|
+
var ENC5 = new TextEncoder();
|
|
655
|
+
function fnmatch2(name, pattern) {
|
|
656
|
+
const re = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\?/g, ".").replace(/\*/g, ".*");
|
|
657
|
+
return new RegExp(`^${re}$`).test(name);
|
|
658
|
+
}
|
|
659
|
+
async function walk(accessor, path, index, maxDepth, depth) {
|
|
660
|
+
if (maxDepth !== null && depth > maxDepth) return [];
|
|
661
|
+
let children;
|
|
662
|
+
try {
|
|
663
|
+
children = await readdir(accessor, path, index ?? void 0);
|
|
664
|
+
} catch {
|
|
665
|
+
return [];
|
|
666
|
+
}
|
|
667
|
+
const results = [];
|
|
668
|
+
for (const child of children) {
|
|
669
|
+
const isFolder = child.endsWith("/");
|
|
670
|
+
const trimmed = isFolder ? child.replace(/\/+$/, "") : child;
|
|
671
|
+
results.push(trimmed);
|
|
672
|
+
if (isFolder) {
|
|
673
|
+
const childSpec = new PathSpec({
|
|
674
|
+
original: trimmed,
|
|
675
|
+
directory: trimmed,
|
|
676
|
+
resolved: false,
|
|
677
|
+
prefix: path.prefix
|
|
678
|
+
});
|
|
679
|
+
const sub = await walk(accessor, childSpec, index, maxDepth, depth + 1);
|
|
680
|
+
results.push(...sub);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
return results;
|
|
684
|
+
}
|
|
685
|
+
async function findCommand(accessor, paths, _texts, opts) {
|
|
686
|
+
const resolved = await resolveGlob(accessor, paths, opts.index ?? void 0);
|
|
687
|
+
const p0 = resolved[0] ?? new PathSpec({
|
|
688
|
+
original: "/",
|
|
689
|
+
directory: "/",
|
|
690
|
+
resolved: false,
|
|
691
|
+
prefix: opts.mountPrefix ?? ""
|
|
692
|
+
});
|
|
693
|
+
const nameFlag = typeof opts.flags.name === "string" ? opts.flags.name : null;
|
|
694
|
+
const inameFlag = typeof opts.flags.iname === "string" ? opts.flags.iname : null;
|
|
695
|
+
const maxDepthRaw = typeof opts.flags.maxdepth === "string" ? opts.flags.maxdepth : null;
|
|
696
|
+
const minDepthRaw = typeof opts.flags.mindepth === "string" ? opts.flags.mindepth : null;
|
|
697
|
+
const maxDepth = maxDepthRaw !== null ? Number.parseInt(maxDepthRaw, 10) : null;
|
|
698
|
+
const minDepth = minDepthRaw !== null ? Number.parseInt(minDepthRaw, 10) : null;
|
|
699
|
+
const allPaths = await walk(accessor, p0, opts.index, maxDepth, 0);
|
|
700
|
+
const searchKey = p0.stripPrefix.replace(/^\/+|\/+$/g, "");
|
|
701
|
+
const baseDepth = searchKey === "" ? -1 : searchKey.split("/").length - 1;
|
|
702
|
+
const results = [];
|
|
703
|
+
for (const p of [...allPaths].sort()) {
|
|
704
|
+
const stripped = p.startsWith(p0.prefix) ? p.slice(p0.prefix.length) : p;
|
|
705
|
+
const trimmed = stripped.replace(/^\/+|\/+$/g, "");
|
|
706
|
+
const depth = trimmed === "" ? -1 : trimmed.split("/").length - (baseDepth + 2);
|
|
707
|
+
if (minDepth !== null && depth < minDepth) continue;
|
|
708
|
+
const entryName = p.split("/").pop() ?? p;
|
|
709
|
+
if (nameFlag !== null && !fnmatch2(entryName, nameFlag)) continue;
|
|
710
|
+
if (inameFlag !== null && !fnmatch2(entryName.toLowerCase(), inameFlag.toLowerCase())) continue;
|
|
711
|
+
results.push(p);
|
|
712
|
+
}
|
|
713
|
+
const out = ENC5.encode(results.join("\n"));
|
|
714
|
+
return [out, new IOResult4()];
|
|
715
|
+
}
|
|
716
|
+
var EMAIL_FIND = command4({
|
|
717
|
+
name: "find",
|
|
718
|
+
resource: ResourceName4.EMAIL,
|
|
719
|
+
spec: specOf4("find"),
|
|
720
|
+
fn: findCommand,
|
|
721
|
+
provision: metadataProvision
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
// src/commands/builtin/email/email_forward.ts
|
|
725
|
+
import {
|
|
726
|
+
CommandSpec,
|
|
727
|
+
IOResult as IOResult5,
|
|
728
|
+
OperandKind,
|
|
729
|
+
Option,
|
|
730
|
+
ResourceName as ResourceName5,
|
|
731
|
+
command as command5
|
|
732
|
+
} from "@struktoai/mirage-core";
|
|
733
|
+
|
|
734
|
+
// src/core/email/send.ts
|
|
735
|
+
import { loadOptionalPeer as loadOptionalPeer3 } from "@struktoai/mirage-core";
|
|
736
|
+
var transporterCache = /* @__PURE__ */ new WeakMap();
|
|
737
|
+
async function getTransporter(config) {
|
|
738
|
+
const existing = transporterCache.get(config);
|
|
739
|
+
if (existing !== void 0) return existing;
|
|
740
|
+
const mod = await loadOptionalPeer3(
|
|
741
|
+
() => import("nodemailer"),
|
|
742
|
+
{ feature: "email send/reply/forward", packageName: "nodemailer" }
|
|
743
|
+
);
|
|
744
|
+
const transporter = mod.createTransport({
|
|
745
|
+
host: config.smtpHost,
|
|
746
|
+
port: config.smtpPort,
|
|
747
|
+
secure: config.smtpPort === 465,
|
|
748
|
+
auth: { user: config.username, pass: config.password }
|
|
749
|
+
});
|
|
750
|
+
transporterCache.set(config, transporter);
|
|
751
|
+
return transporter;
|
|
752
|
+
}
|
|
753
|
+
async function sendMessage(config, to, subject, body) {
|
|
754
|
+
const t = await getTransporter(config);
|
|
755
|
+
await t.sendMail({ from: config.username, to, subject, text: body });
|
|
756
|
+
return { status: "sent", to, subject };
|
|
757
|
+
}
|
|
758
|
+
async function replyMessage(config, original, body) {
|
|
759
|
+
const t = await getTransporter(config);
|
|
760
|
+
const refs = [...original.references];
|
|
761
|
+
if (original.message_id !== "") refs.push(original.message_id);
|
|
762
|
+
const subject = `Re: ${original.subject}`;
|
|
763
|
+
await t.sendMail({
|
|
764
|
+
from: config.username,
|
|
765
|
+
to: original.from.email,
|
|
766
|
+
subject,
|
|
767
|
+
text: body,
|
|
768
|
+
inReplyTo: original.message_id,
|
|
769
|
+
references: refs
|
|
770
|
+
});
|
|
771
|
+
return { status: "sent", to: original.from.email, subject };
|
|
772
|
+
}
|
|
773
|
+
async function replyAllMessage(config, original, body) {
|
|
774
|
+
const t = await getTransporter(config);
|
|
775
|
+
const all = /* @__PURE__ */ new Set();
|
|
776
|
+
if (original.from.email !== "") all.add(original.from.email);
|
|
777
|
+
for (const r of original.to) if (r.email !== "") all.add(r.email);
|
|
778
|
+
for (const r of original.cc) if (r.email !== "") all.add(r.email);
|
|
779
|
+
all.delete(config.username);
|
|
780
|
+
const recipients = [...all].sort();
|
|
781
|
+
const refs = [...original.references];
|
|
782
|
+
if (original.message_id !== "") refs.push(original.message_id);
|
|
783
|
+
const subject = `Re: ${original.subject}`;
|
|
784
|
+
await t.sendMail({
|
|
785
|
+
from: config.username,
|
|
786
|
+
to: recipients,
|
|
787
|
+
subject,
|
|
788
|
+
text: body,
|
|
789
|
+
inReplyTo: original.message_id,
|
|
790
|
+
references: refs
|
|
791
|
+
});
|
|
792
|
+
return { status: "sent", to: recipients, subject };
|
|
793
|
+
}
|
|
794
|
+
async function forwardMessage(config, original, to) {
|
|
795
|
+
const t = await getTransporter(config);
|
|
796
|
+
const subject = `Fwd: ${original.subject}`;
|
|
797
|
+
const fwdBody = `---------- Forwarded message ----------
|
|
798
|
+
From: ${original.from.name} <${original.from.email}>
|
|
799
|
+
Date: ${original.date}
|
|
800
|
+
Subject: ${original.subject}
|
|
801
|
+
|
|
802
|
+
` + original.body_text;
|
|
803
|
+
await t.sendMail({ from: config.username, to, subject, text: fwdBody });
|
|
804
|
+
return { status: "sent", to, subject };
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// src/commands/builtin/email/email_forward.ts
|
|
808
|
+
var ENC6 = new TextEncoder();
|
|
809
|
+
var SPEC = new CommandSpec({
|
|
810
|
+
options: [
|
|
811
|
+
new Option({ long: "--uid", valueKind: OperandKind.TEXT }),
|
|
812
|
+
new Option({ long: "--folder", valueKind: OperandKind.TEXT }),
|
|
813
|
+
new Option({ long: "--to", valueKind: OperandKind.TEXT })
|
|
814
|
+
]
|
|
815
|
+
});
|
|
816
|
+
async function emailForwardCommand(accessor, _paths, _texts, opts) {
|
|
817
|
+
const uid = typeof opts.flags.uid === "string" ? opts.flags.uid : "";
|
|
818
|
+
const folder = typeof opts.flags.folder === "string" ? opts.flags.folder : "";
|
|
819
|
+
const to = typeof opts.flags.to === "string" ? opts.flags.to : "";
|
|
820
|
+
if (uid === "") {
|
|
821
|
+
return [null, new IOResult5({ exitCode: 2, stderr: ENC6.encode("--uid is required\n") })];
|
|
822
|
+
}
|
|
823
|
+
if (folder === "") {
|
|
824
|
+
return [null, new IOResult5({ exitCode: 2, stderr: ENC6.encode("--folder is required\n") })];
|
|
825
|
+
}
|
|
826
|
+
if (to === "") {
|
|
827
|
+
return [null, new IOResult5({ exitCode: 2, stderr: ENC6.encode("--to is required\n") })];
|
|
828
|
+
}
|
|
829
|
+
const original = await fetchMessage(accessor, folder, uid);
|
|
830
|
+
const result = await forwardMessage(accessor.config, original, to);
|
|
831
|
+
const out = ENC6.encode(JSON.stringify(result));
|
|
832
|
+
return [out, new IOResult5()];
|
|
833
|
+
}
|
|
834
|
+
var EMAIL_FORWARD = command5({
|
|
835
|
+
name: "email-forward",
|
|
836
|
+
resource: ResourceName5.EMAIL,
|
|
837
|
+
spec: SPEC,
|
|
838
|
+
fn: emailForwardCommand,
|
|
839
|
+
write: true
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
// src/commands/builtin/email/grep.ts
|
|
843
|
+
import {
|
|
844
|
+
IOResult as IOResult6,
|
|
845
|
+
PathSpec as PathSpec2,
|
|
846
|
+
ResourceName as ResourceName6,
|
|
847
|
+
command as command6,
|
|
848
|
+
compilePattern,
|
|
849
|
+
exitOnEmpty,
|
|
850
|
+
grepFilesOnly,
|
|
851
|
+
grepLines,
|
|
852
|
+
grepStream,
|
|
853
|
+
prefixAggregate,
|
|
854
|
+
quietMatch,
|
|
855
|
+
resolveSource as resolveSource2,
|
|
856
|
+
specOf as specOf5,
|
|
857
|
+
yieldBytes
|
|
858
|
+
} from "@struktoai/mirage-core";
|
|
859
|
+
|
|
860
|
+
// src/core/email/stat.ts
|
|
861
|
+
import { FileStat, FileType } from "@struktoai/mirage-core";
|
|
862
|
+
function guessType(name) {
|
|
863
|
+
const lower = name.toLowerCase();
|
|
864
|
+
if (lower.endsWith(".json") || lower.endsWith(".email.json")) return FileType.JSON;
|
|
865
|
+
if (lower.endsWith(".csv")) return FileType.CSV;
|
|
866
|
+
if (lower.endsWith(".png")) return FileType.IMAGE_PNG;
|
|
867
|
+
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return FileType.IMAGE_JPEG;
|
|
868
|
+
if (lower.endsWith(".gif")) return FileType.IMAGE_GIF;
|
|
869
|
+
if (lower.endsWith(".zip")) return FileType.ZIP;
|
|
870
|
+
if (lower.endsWith(".gz") || lower.endsWith(".gzip")) return FileType.GZIP;
|
|
871
|
+
if (lower.endsWith(".pdf")) return FileType.PDF;
|
|
872
|
+
if (lower.endsWith(".txt") || lower.endsWith(".md") || lower.endsWith(".log")) {
|
|
873
|
+
return FileType.TEXT;
|
|
874
|
+
}
|
|
875
|
+
return FileType.BINARY;
|
|
876
|
+
}
|
|
877
|
+
function enoent3(p) {
|
|
878
|
+
const e = new Error(`ENOENT: ${p}`);
|
|
879
|
+
e.code = "ENOENT";
|
|
880
|
+
return e;
|
|
881
|
+
}
|
|
882
|
+
async function stat(accessor, path, index) {
|
|
883
|
+
const prefix = path.prefix;
|
|
884
|
+
let p = path.original;
|
|
885
|
+
if (prefix !== "" && p.startsWith(prefix)) p = p.slice(prefix.length) || "/";
|
|
886
|
+
const key = p.replace(/^\/+|\/+$/g, "");
|
|
887
|
+
if (key === "") return new FileStat({ name: "/", type: FileType.DIRECTORY });
|
|
888
|
+
if (index === void 0) throw enoent3(path.original);
|
|
889
|
+
const virtualKey = prefix !== "" ? `${prefix}/${key}` : `/${key}`;
|
|
890
|
+
const result = await index.get(virtualKey);
|
|
891
|
+
if (result.entry === void 0 || result.entry === null) {
|
|
892
|
+
if (!key.includes("/")) {
|
|
893
|
+
const folders = await listFolders2(accessor);
|
|
894
|
+
if (folders.includes(key)) return new FileStat({ name: key, type: FileType.DIRECTORY });
|
|
895
|
+
throw enoent3(path.original);
|
|
896
|
+
}
|
|
897
|
+
throw enoent3(path.original);
|
|
898
|
+
}
|
|
899
|
+
const rt = result.entry.resourceType;
|
|
900
|
+
const vfsName = result.entry.vfsName !== "" ? result.entry.vfsName : result.entry.name;
|
|
901
|
+
if (rt === "email/folder") return new FileStat({ name: vfsName, type: FileType.DIRECTORY });
|
|
902
|
+
if (rt === "email/date") return new FileStat({ name: vfsName, type: FileType.DIRECTORY });
|
|
903
|
+
if (rt === "email/message") {
|
|
904
|
+
return new FileStat({
|
|
905
|
+
name: vfsName,
|
|
906
|
+
type: FileType.JSON,
|
|
907
|
+
size: result.entry.size,
|
|
908
|
+
extra: { uid: result.entry.id }
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
if (rt === "email/attachment_dir") {
|
|
912
|
+
return new FileStat({
|
|
913
|
+
name: vfsName,
|
|
914
|
+
type: FileType.DIRECTORY,
|
|
915
|
+
extra: { uid: result.entry.id }
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
if (rt === "email/attachment") {
|
|
919
|
+
return new FileStat({
|
|
920
|
+
name: vfsName,
|
|
921
|
+
type: guessType(vfsName),
|
|
922
|
+
size: result.entry.size,
|
|
923
|
+
extra: { attachment_id: result.entry.id }
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
return new FileStat({ name: vfsName, type: FileType.JSON, extra: { uid: result.entry.id } });
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
// src/core/email/scope.ts
|
|
930
|
+
function detectScope(path) {
|
|
931
|
+
const key = path.stripPrefix.replace(/^\/+|\/+$/g, "");
|
|
932
|
+
if (key === "") {
|
|
933
|
+
return { useNative: false, folder: null, resourcePath: "/" };
|
|
934
|
+
}
|
|
935
|
+
const parts = key.split("/").filter((s) => s !== "");
|
|
936
|
+
if (parts.length === 0) {
|
|
937
|
+
return { useNative: false, folder: null, resourcePath: "/" };
|
|
938
|
+
}
|
|
939
|
+
if (key.endsWith(".email.json")) {
|
|
940
|
+
return { useNative: false, folder: parts[0] ?? null, resourcePath: key };
|
|
941
|
+
}
|
|
942
|
+
if (parts.length <= 2) {
|
|
943
|
+
return { useNative: true, folder: parts[0] ?? null, resourcePath: key };
|
|
944
|
+
}
|
|
945
|
+
return { useNative: false, folder: parts[0] ?? null, resourcePath: key };
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// src/core/email/search.ts
|
|
949
|
+
var TITLE_MAX2 = 80;
|
|
950
|
+
var UNSAFE2 = /[^\w\s\-.]/g;
|
|
951
|
+
var MULTI_UNDERSCORE2 = /_+/g;
|
|
952
|
+
function sanitize2(text) {
|
|
953
|
+
if (text.trim() === "") return "No_Subject";
|
|
954
|
+
let cleaned = text.replace(UNSAFE2, "_").replace(/ /g, "_");
|
|
955
|
+
cleaned = cleaned.replace(MULTI_UNDERSCORE2, "_").replace(/^_+|_+$/g, "");
|
|
956
|
+
if (cleaned.length > TITLE_MAX2) cleaned = `${cleaned.slice(0, TITLE_MAX2 - 3)}...`;
|
|
957
|
+
return cleaned;
|
|
958
|
+
}
|
|
959
|
+
function dateFromHeader2(dateStr) {
|
|
960
|
+
if (dateStr === "") return "1970-01-01";
|
|
961
|
+
const d = new Date(dateStr);
|
|
962
|
+
if (Number.isNaN(d.getTime())) return "1970-01-01";
|
|
963
|
+
const yyyy = d.getUTCFullYear().toString().padStart(4, "0");
|
|
964
|
+
const mm = (d.getUTCMonth() + 1).toString().padStart(2, "0");
|
|
965
|
+
const dd = d.getUTCDate().toString().padStart(2, "0");
|
|
966
|
+
return `${yyyy}-${mm}-${dd}`;
|
|
967
|
+
}
|
|
968
|
+
function buildSearchCriteria(opts) {
|
|
969
|
+
const parts = [];
|
|
970
|
+
if (opts.unseen === true) parts.push("UNSEEN");
|
|
971
|
+
if (opts.text !== void 0 && opts.text !== null && opts.text !== "") {
|
|
972
|
+
parts.push(`TEXT "${opts.text}"`);
|
|
973
|
+
}
|
|
974
|
+
if (opts.subject !== void 0 && opts.subject !== null && opts.subject !== "") {
|
|
975
|
+
parts.push(`SUBJECT "${opts.subject}"`);
|
|
976
|
+
}
|
|
977
|
+
if (opts.fromAddr !== void 0 && opts.fromAddr !== null && opts.fromAddr !== "") {
|
|
978
|
+
parts.push(`FROM "${opts.fromAddr}"`);
|
|
979
|
+
}
|
|
980
|
+
if (opts.toAddr !== void 0 && opts.toAddr !== null && opts.toAddr !== "") {
|
|
981
|
+
parts.push(`TO "${opts.toAddr}"`);
|
|
982
|
+
}
|
|
983
|
+
if (opts.since !== void 0 && opts.since !== null && opts.since !== "") {
|
|
984
|
+
parts.push(`SINCE ${opts.since}`);
|
|
985
|
+
}
|
|
986
|
+
if (opts.before !== void 0 && opts.before !== null && opts.before !== "") {
|
|
987
|
+
parts.push(`BEFORE ${opts.before}`);
|
|
988
|
+
}
|
|
989
|
+
return parts.length > 0 ? parts.join(" ") : "ALL";
|
|
990
|
+
}
|
|
991
|
+
async function searchMessages(accessor, folder, opts = {}, maxResults = null) {
|
|
992
|
+
const criteria = buildSearchCriteria(opts);
|
|
993
|
+
return listMessageUids(accessor, folder, criteria, maxResults);
|
|
994
|
+
}
|
|
995
|
+
function buildVfsPath(prefix, folder, msg) {
|
|
996
|
+
const dateStr = dateFromHeader2(msg.date);
|
|
997
|
+
const subject = sanitize2(msg.subject !== "" ? msg.subject : "No Subject");
|
|
998
|
+
const filename = `${subject}__${msg.uid}.email.json`;
|
|
999
|
+
return [prefix, folder, dateStr, filename].filter((p) => p !== "").join("/");
|
|
1000
|
+
}
|
|
1001
|
+
async function searchAndFormat(accessor, scope, pattern, prefix, maxResults = null) {
|
|
1002
|
+
const folder = scope.folder ?? "";
|
|
1003
|
+
if (folder === "") return [];
|
|
1004
|
+
const uids = await searchMessages(accessor, folder, { text: pattern }, maxResults);
|
|
1005
|
+
const pairs = [];
|
|
1006
|
+
for (const uid of uids) {
|
|
1007
|
+
const msg = await fetchMessage(accessor, folder, uid);
|
|
1008
|
+
const msgText = JSON.stringify(msg);
|
|
1009
|
+
const vfsPath = buildVfsPath(prefix, folder, msg);
|
|
1010
|
+
pairs.push([vfsPath, msgText]);
|
|
1011
|
+
}
|
|
1012
|
+
return pairs;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
// src/commands/builtin/email/grep.ts
|
|
1016
|
+
var ENC7 = new TextEncoder();
|
|
1017
|
+
var DEC = new TextDecoder("utf-8", { fatal: false });
|
|
1018
|
+
function parseFlags(flags) {
|
|
1019
|
+
const toInt = (v) => typeof v === "string" ? Number.parseInt(v, 10) : null;
|
|
1020
|
+
const aCtx = toInt(flags.A);
|
|
1021
|
+
const bCtx = toInt(flags.B);
|
|
1022
|
+
const cCtx = toInt(flags.C);
|
|
1023
|
+
return {
|
|
1024
|
+
ignoreCase: flags.i === true,
|
|
1025
|
+
invert: flags.v === true,
|
|
1026
|
+
lineNumbers: flags.n === true,
|
|
1027
|
+
countOnly: flags.c === true,
|
|
1028
|
+
filesOnly: flags.args_l === true || flags.l === true,
|
|
1029
|
+
wholeWord: flags.w === true,
|
|
1030
|
+
fixedString: flags.F === true,
|
|
1031
|
+
onlyMatching: flags.o === true,
|
|
1032
|
+
maxCount: toInt(flags.m),
|
|
1033
|
+
quiet: flags.q === true,
|
|
1034
|
+
afterContext: aCtx ?? cCtx ?? 0,
|
|
1035
|
+
beforeContext: bCtx ?? cCtx ?? 0
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
function getPattern(texts, flags) {
|
|
1039
|
+
if (typeof flags.e === "string") return flags.e;
|
|
1040
|
+
if (texts.length > 0 && texts[0] !== void 0) return texts[0];
|
|
1041
|
+
throw new Error("grep: usage: grep [flags] pattern [path]");
|
|
1042
|
+
}
|
|
1043
|
+
function splitLinesNoTrailing(text) {
|
|
1044
|
+
const stripped = text.endsWith("\n") ? text.slice(0, -1) : text;
|
|
1045
|
+
return stripped === "" ? [] : stripped.split("\n");
|
|
1046
|
+
}
|
|
1047
|
+
async function grepCommand(accessor, paths, texts, opts) {
|
|
1048
|
+
let pattern;
|
|
1049
|
+
try {
|
|
1050
|
+
pattern = getPattern(texts, opts.flags);
|
|
1051
|
+
} catch (err) {
|
|
1052
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1053
|
+
return [null, new IOResult6({ exitCode: 2, stderr: ENC7.encode(`${msg}
|
|
1054
|
+
`) })];
|
|
1055
|
+
}
|
|
1056
|
+
const f = parseFlags(opts.flags);
|
|
1057
|
+
const recursive = opts.flags.r === true || opts.flags.R === true;
|
|
1058
|
+
if (paths.length > 0) {
|
|
1059
|
+
const first = paths[0];
|
|
1060
|
+
if (first !== void 0) {
|
|
1061
|
+
const scope = detectScope(first);
|
|
1062
|
+
if (scope.useNative) {
|
|
1063
|
+
const filePrefix2 = first.prefix !== "" ? first.prefix : "";
|
|
1064
|
+
const pairs = await searchAndFormat(accessor, scope, pattern, filePrefix2, f.maxCount ?? 50);
|
|
1065
|
+
const lines = [];
|
|
1066
|
+
for (const [vfsPath, msgText] of pairs) {
|
|
1067
|
+
const matched = grepLines(
|
|
1068
|
+
vfsPath,
|
|
1069
|
+
[msgText],
|
|
1070
|
+
compilePattern(pattern, f.ignoreCase, f.fixedString, f.wholeWord),
|
|
1071
|
+
f
|
|
1072
|
+
);
|
|
1073
|
+
for (const line of matched) lines.push(`${vfsPath}:${line}`);
|
|
1074
|
+
}
|
|
1075
|
+
if (lines.length === 0) return [new Uint8Array(0), new IOResult6({ exitCode: 1 })];
|
|
1076
|
+
const out = ENC7.encode(lines.join("\n") + "\n");
|
|
1077
|
+
return [out, new IOResult6()];
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
const resolved = await resolveGlob(accessor, paths, opts.index ?? void 0);
|
|
1081
|
+
if (resolved.length === 0) return [new Uint8Array(0), new IOResult6({ exitCode: 1 })];
|
|
1082
|
+
const filePrefix = resolved[0]?.prefix ?? "";
|
|
1083
|
+
const readdirFn = async (path) => {
|
|
1084
|
+
const spec = new PathSpec2({
|
|
1085
|
+
original: path,
|
|
1086
|
+
directory: path,
|
|
1087
|
+
resolved: false,
|
|
1088
|
+
prefix: filePrefix
|
|
1089
|
+
});
|
|
1090
|
+
return readdir(accessor, spec, opts.index ?? void 0);
|
|
1091
|
+
};
|
|
1092
|
+
const statFn = async (path) => {
|
|
1093
|
+
const spec = new PathSpec2({
|
|
1094
|
+
original: path,
|
|
1095
|
+
directory: path,
|
|
1096
|
+
resolved: false,
|
|
1097
|
+
prefix: filePrefix
|
|
1098
|
+
});
|
|
1099
|
+
return stat(accessor, spec, opts.index ?? void 0);
|
|
1100
|
+
};
|
|
1101
|
+
const readBytesFn = async (path) => {
|
|
1102
|
+
const spec = new PathSpec2({
|
|
1103
|
+
original: path,
|
|
1104
|
+
directory: path,
|
|
1105
|
+
resolved: true,
|
|
1106
|
+
prefix: filePrefix
|
|
1107
|
+
});
|
|
1108
|
+
return read(accessor, spec, opts.index ?? void 0);
|
|
1109
|
+
};
|
|
1110
|
+
if (f.filesOnly) {
|
|
1111
|
+
const warnings = [];
|
|
1112
|
+
const firstResolved2 = resolved[0];
|
|
1113
|
+
if (firstResolved2 === void 0) return [new Uint8Array(0), new IOResult6({ exitCode: 1 })];
|
|
1114
|
+
const results = await grepFilesOnly(
|
|
1115
|
+
readdirFn,
|
|
1116
|
+
statFn,
|
|
1117
|
+
readBytesFn,
|
|
1118
|
+
firstResolved2.original,
|
|
1119
|
+
pattern,
|
|
1120
|
+
{
|
|
1121
|
+
recursive,
|
|
1122
|
+
ignoreCase: f.ignoreCase,
|
|
1123
|
+
invert: f.invert,
|
|
1124
|
+
lineNumbers: f.lineNumbers,
|
|
1125
|
+
countOnly: f.countOnly,
|
|
1126
|
+
fixedString: f.fixedString,
|
|
1127
|
+
onlyMatching: f.onlyMatching,
|
|
1128
|
+
maxCount: f.maxCount,
|
|
1129
|
+
wholeWord: f.wholeWord
|
|
1130
|
+
},
|
|
1131
|
+
warnings
|
|
1132
|
+
);
|
|
1133
|
+
const stderr = warnings.length > 0 ? ENC7.encode(warnings.join("\n")) : null;
|
|
1134
|
+
if (results.length === 0) return [new Uint8Array(0), new IOResult6({ exitCode: 1, stderr })];
|
|
1135
|
+
const out = ENC7.encode(results.join("\n"));
|
|
1136
|
+
return [out, new IOResult6({ stderr })];
|
|
1137
|
+
}
|
|
1138
|
+
const pat2 = compilePattern(pattern, f.ignoreCase, f.fixedString, f.wholeWord);
|
|
1139
|
+
if (resolved.length > 1) {
|
|
1140
|
+
const allResults = [];
|
|
1141
|
+
for (const p of resolved) {
|
|
1142
|
+
const data2 = splitLinesNoTrailing(
|
|
1143
|
+
DEC.decode(await read(accessor, p, opts.index ?? void 0))
|
|
1144
|
+
);
|
|
1145
|
+
const hits = grepLines(p.original, data2, pat2, f);
|
|
1146
|
+
if (f.countOnly) {
|
|
1147
|
+
if (hits.length > 0) allResults.push(`${p.original}:${hits[0] ?? ""}`);
|
|
1148
|
+
} else {
|
|
1149
|
+
for (const h of hits) allResults.push(`${p.original}:${h}`);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
if (allResults.length === 0) return [new Uint8Array(0), new IOResult6({ exitCode: 1 })];
|
|
1153
|
+
const out = ENC7.encode(allResults.join("\n"));
|
|
1154
|
+
return [out, new IOResult6()];
|
|
1155
|
+
}
|
|
1156
|
+
const firstResolved = resolved[0];
|
|
1157
|
+
if (firstResolved === void 0) return [null, new IOResult6()];
|
|
1158
|
+
const data = await read(accessor, firstResolved, opts.index ?? void 0);
|
|
1159
|
+
const source2 = yieldBytes(data);
|
|
1160
|
+
const stream2 = grepStream(source2, pat2, f);
|
|
1161
|
+
if (f.quiet) {
|
|
1162
|
+
const io3 = new IOResult6({ exitCode: 1 });
|
|
1163
|
+
return [quietMatch(stream2, io3), io3];
|
|
1164
|
+
}
|
|
1165
|
+
const io2 = new IOResult6();
|
|
1166
|
+
return [exitOnEmpty(stream2, io2), io2];
|
|
1167
|
+
}
|
|
1168
|
+
let source;
|
|
1169
|
+
try {
|
|
1170
|
+
source = resolveSource2(opts.stdin, "grep: usage: grep [flags] pattern [path]");
|
|
1171
|
+
} catch (err) {
|
|
1172
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1173
|
+
return [null, new IOResult6({ exitCode: 2, stderr: ENC7.encode(`${msg}
|
|
1174
|
+
`) })];
|
|
1175
|
+
}
|
|
1176
|
+
const pat = compilePattern(pattern, f.ignoreCase, f.fixedString, f.wholeWord);
|
|
1177
|
+
const stream = grepStream(source, pat, f);
|
|
1178
|
+
if (f.quiet) {
|
|
1179
|
+
const io2 = new IOResult6({ exitCode: 1 });
|
|
1180
|
+
return [quietMatch(stream, io2), io2];
|
|
1181
|
+
}
|
|
1182
|
+
const io = new IOResult6();
|
|
1183
|
+
return [exitOnEmpty(stream, io), io];
|
|
1184
|
+
}
|
|
1185
|
+
var EMAIL_GREP = command6({
|
|
1186
|
+
name: "grep",
|
|
1187
|
+
resource: ResourceName6.EMAIL,
|
|
1188
|
+
spec: specOf5("grep"),
|
|
1189
|
+
fn: grepCommand,
|
|
1190
|
+
aggregate: prefixAggregate,
|
|
1191
|
+
provision: fileReadProvision
|
|
1192
|
+
});
|
|
1193
|
+
|
|
1194
|
+
// src/commands/builtin/email/head.ts
|
|
1195
|
+
import {
|
|
1196
|
+
IOResult as IOResult7,
|
|
1197
|
+
ResourceName as ResourceName7,
|
|
1198
|
+
command as command7,
|
|
1199
|
+
readStdinAsync,
|
|
1200
|
+
specOf as specOf6
|
|
1201
|
+
} from "@struktoai/mirage-core";
|
|
1202
|
+
var ENC8 = new TextEncoder();
|
|
1203
|
+
function headBytes(data, lines, bytesMode) {
|
|
1204
|
+
if (bytesMode !== null) return data.slice(0, bytesMode);
|
|
1205
|
+
let count = 0;
|
|
1206
|
+
let end = 0;
|
|
1207
|
+
for (let i = 0; i < data.byteLength && count < lines; i++) {
|
|
1208
|
+
if (data[i] === 10) {
|
|
1209
|
+
count += 1;
|
|
1210
|
+
end = i + 1;
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
if (count < lines) return data;
|
|
1214
|
+
return data.slice(0, end - 1);
|
|
1215
|
+
}
|
|
1216
|
+
async function headCommand(accessor, paths, _texts, opts) {
|
|
1217
|
+
const lines = typeof opts.flags.n === "string" ? Number.parseInt(opts.flags.n, 10) : 10;
|
|
1218
|
+
const bytesMode = typeof opts.flags.c === "string" ? Number.parseInt(opts.flags.c, 10) : null;
|
|
1219
|
+
if (paths.length > 0) {
|
|
1220
|
+
const resolved = await resolveGlob(accessor, paths, opts.index ?? void 0);
|
|
1221
|
+
const first = resolved[0];
|
|
1222
|
+
if (first === void 0) return [null, new IOResult7()];
|
|
1223
|
+
const data = await read(accessor, first, opts.index ?? void 0);
|
|
1224
|
+
const out2 = headBytes(data, lines, bytesMode);
|
|
1225
|
+
return [out2, new IOResult7()];
|
|
1226
|
+
}
|
|
1227
|
+
const raw = await readStdinAsync(opts.stdin);
|
|
1228
|
+
if (raw === null) {
|
|
1229
|
+
return [null, new IOResult7({ exitCode: 1, stderr: ENC8.encode("head: missing operand\n") })];
|
|
1230
|
+
}
|
|
1231
|
+
const out = headBytes(raw, lines, bytesMode);
|
|
1232
|
+
return [out, new IOResult7()];
|
|
1233
|
+
}
|
|
1234
|
+
var EMAIL_HEAD = command7({
|
|
1235
|
+
name: "head",
|
|
1236
|
+
resource: ResourceName7.EMAIL,
|
|
1237
|
+
spec: specOf6("head"),
|
|
1238
|
+
fn: headCommand,
|
|
1239
|
+
provision: fileReadProvision
|
|
1240
|
+
});
|
|
1241
|
+
|
|
1242
|
+
// src/commands/builtin/email/jq.ts
|
|
1243
|
+
import {
|
|
1244
|
+
IOResult as IOResult8,
|
|
1245
|
+
ResourceName as ResourceName8,
|
|
1246
|
+
command as command8,
|
|
1247
|
+
jqEval,
|
|
1248
|
+
parseJsonAuto,
|
|
1249
|
+
parseJsonPath,
|
|
1250
|
+
readStdinAsync as readStdinAsync2,
|
|
1251
|
+
specOf as specOf7
|
|
1252
|
+
} from "@struktoai/mirage-core";
|
|
1253
|
+
var ENC9 = new TextEncoder();
|
|
1254
|
+
function formatResult(result, raw, compact) {
|
|
1255
|
+
if (raw && typeof result === "string") return ENC9.encode(result + "\n");
|
|
1256
|
+
const json = compact ? JSON.stringify(result) : JSON.stringify(result, null, 2);
|
|
1257
|
+
return ENC9.encode(json + "\n");
|
|
1258
|
+
}
|
|
1259
|
+
function formatResults(results, raw, compact, spread) {
|
|
1260
|
+
if (spread && Array.isArray(results)) {
|
|
1261
|
+
const parts = [];
|
|
1262
|
+
for (const item of results) parts.push(formatResult(item, raw, compact));
|
|
1263
|
+
let total = 0;
|
|
1264
|
+
for (const p of parts) total += p.byteLength;
|
|
1265
|
+
const out = new Uint8Array(total);
|
|
1266
|
+
let offset = 0;
|
|
1267
|
+
for (const p of parts) {
|
|
1268
|
+
out.set(p, offset);
|
|
1269
|
+
offset += p.byteLength;
|
|
1270
|
+
}
|
|
1271
|
+
return out;
|
|
1272
|
+
}
|
|
1273
|
+
return formatResult(results, raw, compact);
|
|
1274
|
+
}
|
|
1275
|
+
async function jqCommand(accessor, paths, texts, opts) {
|
|
1276
|
+
if (texts.length === 0 || texts[0] === void 0) {
|
|
1277
|
+
return [
|
|
1278
|
+
null,
|
|
1279
|
+
new IOResult8({ exitCode: 2, stderr: ENC9.encode("jq: usage: jq EXPRESSION [path]\n") })
|
|
1280
|
+
];
|
|
1281
|
+
}
|
|
1282
|
+
const expression = texts[0];
|
|
1283
|
+
const r = opts.flags.r === true;
|
|
1284
|
+
const c = opts.flags.c === true;
|
|
1285
|
+
const s = opts.flags.s === true;
|
|
1286
|
+
const spread = expression.includes("[]");
|
|
1287
|
+
if (paths.length > 0) {
|
|
1288
|
+
const resolved = await resolveGlob(accessor, paths, opts.index ?? void 0);
|
|
1289
|
+
const parts = [];
|
|
1290
|
+
for (const p of resolved) {
|
|
1291
|
+
const raw2 = await read(accessor, p, opts.index ?? void 0);
|
|
1292
|
+
let data2 = parseJsonPath(raw2, p.original);
|
|
1293
|
+
if (s) data2 = Array.isArray(data2) ? data2 : [data2];
|
|
1294
|
+
const result3 = await jqEval(data2, expression.trim());
|
|
1295
|
+
parts.push(formatResults(result3, r, c, spread));
|
|
1296
|
+
}
|
|
1297
|
+
let total = 0;
|
|
1298
|
+
for (const part of parts) total += part.byteLength;
|
|
1299
|
+
const out2 = new Uint8Array(total);
|
|
1300
|
+
let offset = 0;
|
|
1301
|
+
for (const part of parts) {
|
|
1302
|
+
out2.set(part, offset);
|
|
1303
|
+
offset += part.byteLength;
|
|
1304
|
+
}
|
|
1305
|
+
const result2 = out2;
|
|
1306
|
+
return [result2, new IOResult8()];
|
|
1307
|
+
}
|
|
1308
|
+
const raw = await readStdinAsync2(opts.stdin);
|
|
1309
|
+
if (raw === null) {
|
|
1310
|
+
return [null, new IOResult8({ exitCode: 2, stderr: ENC9.encode("jq: missing input\n") })];
|
|
1311
|
+
}
|
|
1312
|
+
let data = parseJsonAuto(raw);
|
|
1313
|
+
if (s && !Array.isArray(data)) data = [data];
|
|
1314
|
+
const result = await jqEval(data, expression.trim());
|
|
1315
|
+
const out = formatResults(result, r, c, spread);
|
|
1316
|
+
return [out, new IOResult8()];
|
|
1317
|
+
}
|
|
1318
|
+
var EMAIL_JQ = command8({
|
|
1319
|
+
name: "jq",
|
|
1320
|
+
resource: ResourceName8.EMAIL,
|
|
1321
|
+
spec: specOf7("jq"),
|
|
1322
|
+
fn: jqCommand
|
|
1323
|
+
});
|
|
1324
|
+
|
|
1325
|
+
// src/commands/builtin/email/ls.ts
|
|
1326
|
+
import {
|
|
1327
|
+
FileType as FileType2,
|
|
1328
|
+
IOResult as IOResult9,
|
|
1329
|
+
PathSpec as PathSpec3,
|
|
1330
|
+
ResourceName as ResourceName9,
|
|
1331
|
+
command as command9,
|
|
1332
|
+
humanSize,
|
|
1333
|
+
specOf as specOf8
|
|
1334
|
+
} from "@struktoai/mirage-core";
|
|
1335
|
+
var ENC10 = new TextEncoder();
|
|
1336
|
+
async function lsEntries(accessor, path, allFiles, sortBy, reverse, recursive, listDir, warnings, indexCache) {
|
|
1337
|
+
if (listDir) {
|
|
1338
|
+
const s = await stat(accessor, path, indexCache ?? void 0);
|
|
1339
|
+
return [s];
|
|
1340
|
+
}
|
|
1341
|
+
let entries;
|
|
1342
|
+
try {
|
|
1343
|
+
entries = await readdir(accessor, path, indexCache ?? void 0);
|
|
1344
|
+
} catch (err) {
|
|
1345
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1346
|
+
warnings.push(`ls: cannot access '${path.original}': ${msg}`);
|
|
1347
|
+
return [];
|
|
1348
|
+
}
|
|
1349
|
+
const stats = [];
|
|
1350
|
+
for (const entry of entries) {
|
|
1351
|
+
try {
|
|
1352
|
+
const eSpec = new PathSpec3({
|
|
1353
|
+
original: entry,
|
|
1354
|
+
directory: entry,
|
|
1355
|
+
resolved: false,
|
|
1356
|
+
prefix: path.prefix
|
|
1357
|
+
});
|
|
1358
|
+
const s = await stat(accessor, eSpec, indexCache ?? void 0);
|
|
1359
|
+
if (!allFiles && s.name.startsWith(".")) continue;
|
|
1360
|
+
stats.push(s);
|
|
1361
|
+
} catch (err) {
|
|
1362
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1363
|
+
warnings.push(`ls: cannot access '${entry}': ${msg}`);
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
if (sortBy === "size") {
|
|
1367
|
+
stats.sort((a, b) => (a.size ?? 0) - (b.size ?? 0));
|
|
1368
|
+
if (!reverse) stats.reverse();
|
|
1369
|
+
} else {
|
|
1370
|
+
stats.sort((a, b) => a.name.localeCompare(b.name));
|
|
1371
|
+
if (reverse) stats.reverse();
|
|
1372
|
+
}
|
|
1373
|
+
if (recursive) {
|
|
1374
|
+
const subEntries = [];
|
|
1375
|
+
for (const s of stats) {
|
|
1376
|
+
subEntries.push(s);
|
|
1377
|
+
if (s.type === FileType2.DIRECTORY) {
|
|
1378
|
+
const childPath = path.child(s.name);
|
|
1379
|
+
const childSpec = new PathSpec3({
|
|
1380
|
+
original: childPath,
|
|
1381
|
+
directory: childPath,
|
|
1382
|
+
resolved: false,
|
|
1383
|
+
prefix: path.prefix
|
|
1384
|
+
});
|
|
1385
|
+
const sub = await lsEntries(
|
|
1386
|
+
accessor,
|
|
1387
|
+
childSpec,
|
|
1388
|
+
allFiles,
|
|
1389
|
+
sortBy,
|
|
1390
|
+
reverse,
|
|
1391
|
+
recursive,
|
|
1392
|
+
false,
|
|
1393
|
+
warnings,
|
|
1394
|
+
indexCache
|
|
1395
|
+
);
|
|
1396
|
+
subEntries.push(...sub);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
return subEntries;
|
|
1400
|
+
}
|
|
1401
|
+
return stats;
|
|
1402
|
+
}
|
|
1403
|
+
async function lsCommand(accessor, paths, _texts, opts) {
|
|
1404
|
+
const resolved = await resolveGlob(accessor, paths, opts.index ?? void 0);
|
|
1405
|
+
const targets = resolved.length > 0 ? resolved : [
|
|
1406
|
+
new PathSpec3({
|
|
1407
|
+
original: opts.cwd,
|
|
1408
|
+
directory: opts.cwd,
|
|
1409
|
+
resolved: false,
|
|
1410
|
+
prefix: opts.mountPrefix ?? ""
|
|
1411
|
+
})
|
|
1412
|
+
];
|
|
1413
|
+
const long = opts.flags.args_l === true;
|
|
1414
|
+
const allFiles = opts.flags.a === true || opts.flags.A === true;
|
|
1415
|
+
const human = opts.flags.h === true;
|
|
1416
|
+
const reverse = opts.flags.r === true;
|
|
1417
|
+
const recursive = opts.flags.R === true;
|
|
1418
|
+
const listDir = opts.flags.d === true;
|
|
1419
|
+
const classify = opts.flags.F === true;
|
|
1420
|
+
const sortBy = opts.flags.S === true ? "size" : "name";
|
|
1421
|
+
const warnings = [];
|
|
1422
|
+
const results = [];
|
|
1423
|
+
for (const p of targets) {
|
|
1424
|
+
let entries;
|
|
1425
|
+
try {
|
|
1426
|
+
entries = await lsEntries(
|
|
1427
|
+
accessor,
|
|
1428
|
+
p,
|
|
1429
|
+
allFiles,
|
|
1430
|
+
sortBy,
|
|
1431
|
+
reverse,
|
|
1432
|
+
recursive,
|
|
1433
|
+
listDir,
|
|
1434
|
+
warnings,
|
|
1435
|
+
opts.index
|
|
1436
|
+
);
|
|
1437
|
+
} catch (err) {
|
|
1438
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1439
|
+
warnings.push(`ls: cannot access '${p.original}': ${msg}`);
|
|
1440
|
+
continue;
|
|
1441
|
+
}
|
|
1442
|
+
if (long) {
|
|
1443
|
+
for (const e of entries) {
|
|
1444
|
+
const sizeStr = human ? humanSize(e.size ?? 0) : String(e.size ?? 0);
|
|
1445
|
+
results.push(`${e.type ?? "-"} ${sizeStr} ${e.modified ?? ""} ${e.name}`);
|
|
1446
|
+
}
|
|
1447
|
+
} else {
|
|
1448
|
+
for (const e of entries) {
|
|
1449
|
+
const isDir = classify && e.type === FileType2.DIRECTORY;
|
|
1450
|
+
const name = isDir ? e.name + "/" : e.name;
|
|
1451
|
+
results.push(name);
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
const stderr = warnings.length > 0 ? ENC10.encode(warnings.join("\n")) : null;
|
|
1456
|
+
const exitCode = warnings.length > 0 && results.length === 0 ? 1 : 0;
|
|
1457
|
+
const out = ENC10.encode(results.join("\n"));
|
|
1458
|
+
return [out, new IOResult9({ stderr, exitCode })];
|
|
1459
|
+
}
|
|
1460
|
+
var EMAIL_LS = command9({
|
|
1461
|
+
name: "ls",
|
|
1462
|
+
resource: ResourceName9.EMAIL,
|
|
1463
|
+
spec: specOf8("ls"),
|
|
1464
|
+
fn: lsCommand,
|
|
1465
|
+
provision: metadataProvision
|
|
1466
|
+
});
|
|
1467
|
+
|
|
1468
|
+
// src/commands/builtin/email/nl.ts
|
|
1469
|
+
import {
|
|
1470
|
+
IOResult as IOResult10,
|
|
1471
|
+
ResourceName as ResourceName10,
|
|
1472
|
+
command as command10,
|
|
1473
|
+
readStdinAsync as readStdinAsync3,
|
|
1474
|
+
specOf as specOf9
|
|
1475
|
+
} from "@struktoai/mirage-core";
|
|
1476
|
+
var ENC11 = new TextEncoder();
|
|
1477
|
+
var DEC2 = new TextDecoder("utf-8", { fatal: false });
|
|
1478
|
+
function shouldNumber(line, mode, pattern) {
|
|
1479
|
+
if (mode === "n") return false;
|
|
1480
|
+
if (mode === "a") return true;
|
|
1481
|
+
if (mode === "p" && pattern !== null) return pattern.test(line);
|
|
1482
|
+
return line.trim() !== "";
|
|
1483
|
+
}
|
|
1484
|
+
function pad(n, width) {
|
|
1485
|
+
const s = String(n);
|
|
1486
|
+
return s.length >= width ? s : " ".repeat(width - s.length) + s;
|
|
1487
|
+
}
|
|
1488
|
+
async function nlCommand(accessor, paths, _texts, opts) {
|
|
1489
|
+
const bRaw = typeof opts.flags.b === "string" ? opts.flags.b : "t";
|
|
1490
|
+
let mode = bRaw;
|
|
1491
|
+
let pattern = null;
|
|
1492
|
+
if (bRaw.startsWith("p")) {
|
|
1493
|
+
mode = "p";
|
|
1494
|
+
pattern = new RegExp(bRaw.slice(1));
|
|
1495
|
+
}
|
|
1496
|
+
const start = typeof opts.flags.v === "string" ? Number.parseInt(opts.flags.v, 10) : 1;
|
|
1497
|
+
const increment = typeof opts.flags.i === "string" ? Number.parseInt(opts.flags.i, 10) : 1;
|
|
1498
|
+
const width = typeof opts.flags.w === "string" ? Number.parseInt(opts.flags.w, 10) : 6;
|
|
1499
|
+
const separator = typeof opts.flags.s === "string" ? opts.flags.s : " ";
|
|
1500
|
+
let raw = null;
|
|
1501
|
+
if (paths.length > 0) {
|
|
1502
|
+
const resolved = await resolveGlob(accessor, paths, opts.index ?? void 0);
|
|
1503
|
+
const first = resolved[0];
|
|
1504
|
+
if (first === void 0) return [null, new IOResult10()];
|
|
1505
|
+
raw = await read(accessor, first, opts.index ?? void 0);
|
|
1506
|
+
} else {
|
|
1507
|
+
raw = await readStdinAsync3(opts.stdin);
|
|
1508
|
+
if (raw === null) {
|
|
1509
|
+
return [null, new IOResult10({ exitCode: 1, stderr: ENC11.encode("nl: missing operand\n") })];
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
let num = start;
|
|
1513
|
+
const outLines = [];
|
|
1514
|
+
for (const line of DEC2.decode(raw).split("\n")) {
|
|
1515
|
+
if (shouldNumber(line, mode, pattern)) {
|
|
1516
|
+
outLines.push(`${pad(num, width)}${separator}${line}`);
|
|
1517
|
+
num += increment;
|
|
1518
|
+
} else {
|
|
1519
|
+
outLines.push(`${" ".repeat(width)}${separator}${line}`);
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
const out = ENC11.encode(outLines.join("\n") + "\n");
|
|
1523
|
+
return [out, new IOResult10()];
|
|
1524
|
+
}
|
|
1525
|
+
var EMAIL_NL = command10({
|
|
1526
|
+
name: "nl",
|
|
1527
|
+
resource: ResourceName10.EMAIL,
|
|
1528
|
+
spec: specOf9("nl"),
|
|
1529
|
+
fn: nlCommand
|
|
1530
|
+
});
|
|
1531
|
+
|
|
1532
|
+
// src/commands/builtin/email/email_read.ts
|
|
1533
|
+
import {
|
|
1534
|
+
CommandSpec as CommandSpec2,
|
|
1535
|
+
IOResult as IOResult11,
|
|
1536
|
+
OperandKind as OperandKind2,
|
|
1537
|
+
Option as Option2,
|
|
1538
|
+
ResourceName as ResourceName11,
|
|
1539
|
+
command as command11
|
|
1540
|
+
} from "@struktoai/mirage-core";
|
|
1541
|
+
var ENC12 = new TextEncoder();
|
|
1542
|
+
var SPEC2 = new CommandSpec2({
|
|
1543
|
+
options: [
|
|
1544
|
+
new Option2({ long: "--uid", valueKind: OperandKind2.TEXT }),
|
|
1545
|
+
new Option2({ long: "--folder", valueKind: OperandKind2.TEXT })
|
|
1546
|
+
]
|
|
1547
|
+
});
|
|
1548
|
+
async function emailReadCommand(accessor, _paths, _texts, opts) {
|
|
1549
|
+
const uid = typeof opts.flags.uid === "string" ? opts.flags.uid : "";
|
|
1550
|
+
const folder = typeof opts.flags.folder === "string" ? opts.flags.folder : "";
|
|
1551
|
+
if (uid === "") {
|
|
1552
|
+
return [null, new IOResult11({ exitCode: 2, stderr: ENC12.encode("--uid is required\n") })];
|
|
1553
|
+
}
|
|
1554
|
+
if (folder === "") {
|
|
1555
|
+
return [null, new IOResult11({ exitCode: 2, stderr: ENC12.encode("--folder is required\n") })];
|
|
1556
|
+
}
|
|
1557
|
+
const processed = await fetchMessage(accessor, folder, uid);
|
|
1558
|
+
const out = ENC12.encode(JSON.stringify(processed));
|
|
1559
|
+
return [out, new IOResult11()];
|
|
1560
|
+
}
|
|
1561
|
+
var EMAIL_READ = command11({
|
|
1562
|
+
name: "email-read",
|
|
1563
|
+
resource: ResourceName11.EMAIL,
|
|
1564
|
+
spec: SPEC2,
|
|
1565
|
+
fn: emailReadCommand
|
|
1566
|
+
});
|
|
1567
|
+
|
|
1568
|
+
// src/commands/builtin/email/realpath.ts
|
|
1569
|
+
import {
|
|
1570
|
+
IOResult as IOResult12,
|
|
1571
|
+
PathSpec as PathSpec4,
|
|
1572
|
+
ResourceName as ResourceName12,
|
|
1573
|
+
command as command12,
|
|
1574
|
+
specOf as specOf10
|
|
1575
|
+
} from "@struktoai/mirage-core";
|
|
1576
|
+
var ENC13 = new TextEncoder();
|
|
1577
|
+
function normalize(p) {
|
|
1578
|
+
const isAbs = p.startsWith("/");
|
|
1579
|
+
const segments = p.split("/").filter((s) => s !== "" && s !== ".");
|
|
1580
|
+
const out = [];
|
|
1581
|
+
for (const s of segments) {
|
|
1582
|
+
if (s === "..") {
|
|
1583
|
+
if (out.length > 0) out.pop();
|
|
1584
|
+
else if (!isAbs) out.push("..");
|
|
1585
|
+
} else {
|
|
1586
|
+
out.push(s);
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
const joined = out.join("/");
|
|
1590
|
+
return isAbs ? "/" + joined : joined === "" ? "." : joined;
|
|
1591
|
+
}
|
|
1592
|
+
async function exists(accessor, path, index, prefix) {
|
|
1593
|
+
try {
|
|
1594
|
+
const spec = new PathSpec4({ original: path, directory: path, prefix });
|
|
1595
|
+
await stat(accessor, spec, index ?? void 0);
|
|
1596
|
+
return true;
|
|
1597
|
+
} catch {
|
|
1598
|
+
return false;
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
async function realpathCommand(accessor, paths, _texts, opts) {
|
|
1602
|
+
const prefix = opts.mountPrefix ?? "";
|
|
1603
|
+
const e = opts.flags.e === true;
|
|
1604
|
+
const lines = [];
|
|
1605
|
+
for (const p of paths) {
|
|
1606
|
+
const full = prefix !== "" ? prefix + p.original : p.original;
|
|
1607
|
+
const resolved = normalize(full);
|
|
1608
|
+
if (e && !await exists(accessor, resolved, opts.index, prefix)) {
|
|
1609
|
+
const msg = `realpath: '${p.original}': No such file or directory
|
|
1610
|
+
`;
|
|
1611
|
+
return [null, new IOResult12({ exitCode: 1, stderr: ENC13.encode(msg) })];
|
|
1612
|
+
}
|
|
1613
|
+
lines.push(resolved);
|
|
1614
|
+
}
|
|
1615
|
+
const out = ENC13.encode(lines.join("\n") + "\n");
|
|
1616
|
+
return [out, new IOResult12()];
|
|
1617
|
+
}
|
|
1618
|
+
var EMAIL_REALPATH = command12({
|
|
1619
|
+
name: "realpath",
|
|
1620
|
+
resource: ResourceName12.EMAIL,
|
|
1621
|
+
spec: specOf10("realpath"),
|
|
1622
|
+
fn: realpathCommand
|
|
1623
|
+
});
|
|
1624
|
+
|
|
1625
|
+
// src/commands/builtin/email/email_reply.ts
|
|
1626
|
+
import {
|
|
1627
|
+
CommandSpec as CommandSpec3,
|
|
1628
|
+
IOResult as IOResult13,
|
|
1629
|
+
OperandKind as OperandKind3,
|
|
1630
|
+
Option as Option3,
|
|
1631
|
+
ResourceName as ResourceName13,
|
|
1632
|
+
command as command13
|
|
1633
|
+
} from "@struktoai/mirage-core";
|
|
1634
|
+
var ENC14 = new TextEncoder();
|
|
1635
|
+
var SPEC3 = new CommandSpec3({
|
|
1636
|
+
options: [
|
|
1637
|
+
new Option3({ long: "--uid", valueKind: OperandKind3.TEXT }),
|
|
1638
|
+
new Option3({ long: "--folder", valueKind: OperandKind3.TEXT }),
|
|
1639
|
+
new Option3({ long: "--body", valueKind: OperandKind3.TEXT })
|
|
1640
|
+
]
|
|
1641
|
+
});
|
|
1642
|
+
async function emailReplyCommand(accessor, _paths, _texts, opts) {
|
|
1643
|
+
const uid = typeof opts.flags.uid === "string" ? opts.flags.uid : "";
|
|
1644
|
+
const folder = typeof opts.flags.folder === "string" ? opts.flags.folder : "";
|
|
1645
|
+
const body = typeof opts.flags.body === "string" ? opts.flags.body : "";
|
|
1646
|
+
if (uid === "") {
|
|
1647
|
+
return [null, new IOResult13({ exitCode: 2, stderr: ENC14.encode("--uid is required\n") })];
|
|
1648
|
+
}
|
|
1649
|
+
if (folder === "") {
|
|
1650
|
+
return [null, new IOResult13({ exitCode: 2, stderr: ENC14.encode("--folder is required\n") })];
|
|
1651
|
+
}
|
|
1652
|
+
if (body === "") {
|
|
1653
|
+
return [null, new IOResult13({ exitCode: 2, stderr: ENC14.encode("--body is required\n") })];
|
|
1654
|
+
}
|
|
1655
|
+
const original = await fetchMessage(accessor, folder, uid);
|
|
1656
|
+
const result = await replyMessage(accessor.config, original, body);
|
|
1657
|
+
const out = ENC14.encode(JSON.stringify(result));
|
|
1658
|
+
return [out, new IOResult13()];
|
|
1659
|
+
}
|
|
1660
|
+
var EMAIL_REPLY = command13({
|
|
1661
|
+
name: "email-reply",
|
|
1662
|
+
resource: ResourceName13.EMAIL,
|
|
1663
|
+
spec: SPEC3,
|
|
1664
|
+
fn: emailReplyCommand,
|
|
1665
|
+
write: true
|
|
1666
|
+
});
|
|
1667
|
+
|
|
1668
|
+
// src/commands/builtin/email/email_reply_all.ts
|
|
1669
|
+
import {
|
|
1670
|
+
CommandSpec as CommandSpec4,
|
|
1671
|
+
IOResult as IOResult14,
|
|
1672
|
+
OperandKind as OperandKind4,
|
|
1673
|
+
Option as Option4,
|
|
1674
|
+
ResourceName as ResourceName14,
|
|
1675
|
+
command as command14
|
|
1676
|
+
} from "@struktoai/mirage-core";
|
|
1677
|
+
var ENC15 = new TextEncoder();
|
|
1678
|
+
var SPEC4 = new CommandSpec4({
|
|
1679
|
+
options: [
|
|
1680
|
+
new Option4({ long: "--uid", valueKind: OperandKind4.TEXT }),
|
|
1681
|
+
new Option4({ long: "--folder", valueKind: OperandKind4.TEXT }),
|
|
1682
|
+
new Option4({ long: "--body", valueKind: OperandKind4.TEXT })
|
|
1683
|
+
]
|
|
1684
|
+
});
|
|
1685
|
+
async function emailReplyAllCommand(accessor, _paths, _texts, opts) {
|
|
1686
|
+
const uid = typeof opts.flags.uid === "string" ? opts.flags.uid : "";
|
|
1687
|
+
const folder = typeof opts.flags.folder === "string" ? opts.flags.folder : "";
|
|
1688
|
+
const body = typeof opts.flags.body === "string" ? opts.flags.body : "";
|
|
1689
|
+
if (uid === "") {
|
|
1690
|
+
return [null, new IOResult14({ exitCode: 2, stderr: ENC15.encode("--uid is required\n") })];
|
|
1691
|
+
}
|
|
1692
|
+
if (folder === "") {
|
|
1693
|
+
return [null, new IOResult14({ exitCode: 2, stderr: ENC15.encode("--folder is required\n") })];
|
|
1694
|
+
}
|
|
1695
|
+
if (body === "") {
|
|
1696
|
+
return [null, new IOResult14({ exitCode: 2, stderr: ENC15.encode("--body is required\n") })];
|
|
1697
|
+
}
|
|
1698
|
+
const original = await fetchMessage(accessor, folder, uid);
|
|
1699
|
+
const result = await replyAllMessage(accessor.config, original, body);
|
|
1700
|
+
const out = ENC15.encode(JSON.stringify(result));
|
|
1701
|
+
return [out, new IOResult14()];
|
|
1702
|
+
}
|
|
1703
|
+
var EMAIL_REPLY_ALL = command14({
|
|
1704
|
+
name: "email-reply-all",
|
|
1705
|
+
resource: ResourceName14.EMAIL,
|
|
1706
|
+
spec: SPEC4,
|
|
1707
|
+
fn: emailReplyAllCommand,
|
|
1708
|
+
write: true
|
|
1709
|
+
});
|
|
1710
|
+
|
|
1711
|
+
// src/commands/builtin/email/rg.ts
|
|
1712
|
+
import {
|
|
1713
|
+
IOResult as IOResult15,
|
|
1714
|
+
PathSpec as PathSpec5,
|
|
1715
|
+
ResourceName as ResourceName15,
|
|
1716
|
+
command as command15,
|
|
1717
|
+
compilePattern as compilePattern2,
|
|
1718
|
+
grepLines as grepLines2,
|
|
1719
|
+
readStdinAsync as readStdinAsync4,
|
|
1720
|
+
specOf as specOf11
|
|
1721
|
+
} from "@struktoai/mirage-core";
|
|
1722
|
+
var ENC16 = new TextEncoder();
|
|
1723
|
+
var DEC3 = new TextDecoder("utf-8", { fatal: false });
|
|
1724
|
+
async function collectFiles(accessor, path, index) {
|
|
1725
|
+
if (path.original.endsWith(".json") || path.original.endsWith(".jsonl")) {
|
|
1726
|
+
return [path.original];
|
|
1727
|
+
}
|
|
1728
|
+
let children;
|
|
1729
|
+
try {
|
|
1730
|
+
children = await readdir(accessor, path, index ?? void 0);
|
|
1731
|
+
} catch {
|
|
1732
|
+
return [];
|
|
1733
|
+
}
|
|
1734
|
+
const files = [];
|
|
1735
|
+
for (const child of children) {
|
|
1736
|
+
if (child.endsWith(".email.json")) {
|
|
1737
|
+
files.push(child);
|
|
1738
|
+
} else {
|
|
1739
|
+
const childSpec = new PathSpec5({
|
|
1740
|
+
original: child,
|
|
1741
|
+
directory: child,
|
|
1742
|
+
resolved: false,
|
|
1743
|
+
prefix: path.prefix
|
|
1744
|
+
});
|
|
1745
|
+
files.push(...await collectFiles(accessor, childSpec, index));
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
return files;
|
|
1749
|
+
}
|
|
1750
|
+
async function rgCommand(accessor, paths, texts, opts) {
|
|
1751
|
+
if (texts.length === 0 || texts[0] === void 0) {
|
|
1752
|
+
return [
|
|
1753
|
+
null,
|
|
1754
|
+
new IOResult15({ exitCode: 2, stderr: ENC16.encode("rg: usage: rg [flags] pattern [path]\n") })
|
|
1755
|
+
];
|
|
1756
|
+
}
|
|
1757
|
+
const pattern = texts[0];
|
|
1758
|
+
const ignoreCase = opts.flags.i === true;
|
|
1759
|
+
const invert = opts.flags.v === true;
|
|
1760
|
+
const lineNumbers = opts.flags.n === true;
|
|
1761
|
+
const countOnly = opts.flags.c === true;
|
|
1762
|
+
const filesOnly = opts.flags.args_l === true || opts.flags.l === true;
|
|
1763
|
+
const wholeWord = opts.flags.w === true;
|
|
1764
|
+
const fixedString = opts.flags.F === true;
|
|
1765
|
+
const onlyMatching = opts.flags.o === true;
|
|
1766
|
+
const maxCount = typeof opts.flags.m === "string" ? Number.parseInt(opts.flags.m, 10) : null;
|
|
1767
|
+
const hidden = opts.flags.hidden === true;
|
|
1768
|
+
const pat = compilePattern2(pattern, ignoreCase, fixedString, wholeWord);
|
|
1769
|
+
const lineOpts = {
|
|
1770
|
+
invert,
|
|
1771
|
+
lineNumbers,
|
|
1772
|
+
countOnly,
|
|
1773
|
+
filesOnly,
|
|
1774
|
+
onlyMatching,
|
|
1775
|
+
maxCount
|
|
1776
|
+
};
|
|
1777
|
+
if (paths.length > 0) {
|
|
1778
|
+
const first = paths[0];
|
|
1779
|
+
if (first !== void 0) {
|
|
1780
|
+
const scope = detectScope(first);
|
|
1781
|
+
if (scope.useNative) {
|
|
1782
|
+
const filePrefix2 = first.prefix !== "" ? first.prefix : "";
|
|
1783
|
+
const pairs = await searchAndFormat(accessor, scope, pattern, filePrefix2, maxCount ?? 50);
|
|
1784
|
+
const lines2 = [];
|
|
1785
|
+
for (const [vfsPath, msgText] of pairs) {
|
|
1786
|
+
const matched2 = grepLines2(vfsPath, [msgText], pat, lineOpts);
|
|
1787
|
+
for (const line of matched2) lines2.push(`${vfsPath}:${line}`);
|
|
1788
|
+
}
|
|
1789
|
+
if (lines2.length === 0) return [new Uint8Array(0), new IOResult15({ exitCode: 1 })];
|
|
1790
|
+
const out3 = ENC16.encode(lines2.join("\n") + "\n");
|
|
1791
|
+
return [out3, new IOResult15()];
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
const resolved = await resolveGlob(accessor, paths, opts.index ?? void 0);
|
|
1795
|
+
const filePrefix = resolved[0]?.prefix ?? "";
|
|
1796
|
+
const blobPaths = [];
|
|
1797
|
+
for (const p of resolved) blobPaths.push(...await collectFiles(accessor, p, opts.index));
|
|
1798
|
+
const sortedUnique = [...new Set(blobPaths)].sort();
|
|
1799
|
+
const allResults = [];
|
|
1800
|
+
let anyMatch = false;
|
|
1801
|
+
for (const bp of sortedUnique) {
|
|
1802
|
+
if (!hidden && bp.split("/").some((part) => part.startsWith("."))) continue;
|
|
1803
|
+
let data;
|
|
1804
|
+
try {
|
|
1805
|
+
const spec = new PathSpec5({
|
|
1806
|
+
original: bp,
|
|
1807
|
+
directory: bp,
|
|
1808
|
+
resolved: true,
|
|
1809
|
+
prefix: filePrefix
|
|
1810
|
+
});
|
|
1811
|
+
data = await read(accessor, spec, opts.index ?? void 0);
|
|
1812
|
+
} catch {
|
|
1813
|
+
continue;
|
|
1814
|
+
}
|
|
1815
|
+
const text = DEC3.decode(data);
|
|
1816
|
+
if (text === "") continue;
|
|
1817
|
+
const lines2 = text.split("\n");
|
|
1818
|
+
if (lines2.length > 0 && lines2[lines2.length - 1] === "") lines2.pop();
|
|
1819
|
+
const matched2 = grepLines2(bp, lines2, pat, lineOpts);
|
|
1820
|
+
if (matched2.length === 0) continue;
|
|
1821
|
+
anyMatch = true;
|
|
1822
|
+
if (filesOnly) {
|
|
1823
|
+
allResults.push(bp);
|
|
1824
|
+
continue;
|
|
1825
|
+
}
|
|
1826
|
+
if (countOnly) {
|
|
1827
|
+
allResults.push(`${bp}:${String(matched2.length)}`);
|
|
1828
|
+
continue;
|
|
1829
|
+
}
|
|
1830
|
+
for (const line of matched2) allResults.push(`${bp}:${line}`);
|
|
1831
|
+
}
|
|
1832
|
+
if (!anyMatch) return [new Uint8Array(0), new IOResult15({ exitCode: 1 })];
|
|
1833
|
+
const out2 = ENC16.encode(allResults.join("\n"));
|
|
1834
|
+
return [out2, new IOResult15()];
|
|
1835
|
+
}
|
|
1836
|
+
const raw = await readStdinAsync4(opts.stdin);
|
|
1837
|
+
if (raw === null) {
|
|
1838
|
+
return [
|
|
1839
|
+
null,
|
|
1840
|
+
new IOResult15({ exitCode: 2, stderr: ENC16.encode("rg: usage: rg [flags] pattern path\n") })
|
|
1841
|
+
];
|
|
1842
|
+
}
|
|
1843
|
+
const lines = DEC3.decode(raw).split("\n");
|
|
1844
|
+
if (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
1845
|
+
const matched = grepLines2("<stdin>", lines, pat, lineOpts);
|
|
1846
|
+
if (matched.length === 0) return [new Uint8Array(0), new IOResult15({ exitCode: 1 })];
|
|
1847
|
+
if (countOnly) return [ENC16.encode(String(matched.length)), new IOResult15()];
|
|
1848
|
+
const out = ENC16.encode(matched.join("\n"));
|
|
1849
|
+
return [out, new IOResult15()];
|
|
1850
|
+
}
|
|
1851
|
+
var EMAIL_RG = command15({
|
|
1852
|
+
name: "rg",
|
|
1853
|
+
resource: ResourceName15.EMAIL,
|
|
1854
|
+
spec: specOf11("rg"),
|
|
1855
|
+
fn: rgCommand
|
|
1856
|
+
});
|
|
1857
|
+
|
|
1858
|
+
// src/commands/builtin/email/email_send.ts
|
|
1859
|
+
import {
|
|
1860
|
+
CommandSpec as CommandSpec5,
|
|
1861
|
+
IOResult as IOResult16,
|
|
1862
|
+
OperandKind as OperandKind5,
|
|
1863
|
+
Option as Option5,
|
|
1864
|
+
ResourceName as ResourceName16,
|
|
1865
|
+
command as command16
|
|
1866
|
+
} from "@struktoai/mirage-core";
|
|
1867
|
+
var ENC17 = new TextEncoder();
|
|
1868
|
+
var SPEC5 = new CommandSpec5({
|
|
1869
|
+
options: [
|
|
1870
|
+
new Option5({ long: "--to", valueKind: OperandKind5.TEXT }),
|
|
1871
|
+
new Option5({ long: "--subject", valueKind: OperandKind5.TEXT }),
|
|
1872
|
+
new Option5({ long: "--body", valueKind: OperandKind5.TEXT })
|
|
1873
|
+
]
|
|
1874
|
+
});
|
|
1875
|
+
async function emailSendCommand(accessor, _paths, _texts, opts) {
|
|
1876
|
+
const to = typeof opts.flags.to === "string" ? opts.flags.to : "";
|
|
1877
|
+
const subject = typeof opts.flags.subject === "string" ? opts.flags.subject : "";
|
|
1878
|
+
const body = typeof opts.flags.body === "string" ? opts.flags.body : "";
|
|
1879
|
+
if (to === "") {
|
|
1880
|
+
return [null, new IOResult16({ exitCode: 2, stderr: ENC17.encode("--to is required\n") })];
|
|
1881
|
+
}
|
|
1882
|
+
if (subject === "") {
|
|
1883
|
+
return [null, new IOResult16({ exitCode: 2, stderr: ENC17.encode("--subject is required\n") })];
|
|
1884
|
+
}
|
|
1885
|
+
if (body === "") {
|
|
1886
|
+
return [null, new IOResult16({ exitCode: 2, stderr: ENC17.encode("--body is required\n") })];
|
|
1887
|
+
}
|
|
1888
|
+
const result = await sendMessage(accessor.config, to, subject, body);
|
|
1889
|
+
const out = ENC17.encode(JSON.stringify(result));
|
|
1890
|
+
return [out, new IOResult16()];
|
|
1891
|
+
}
|
|
1892
|
+
var EMAIL_SEND = command16({
|
|
1893
|
+
name: "email-send",
|
|
1894
|
+
resource: ResourceName16.EMAIL,
|
|
1895
|
+
spec: SPEC5,
|
|
1896
|
+
fn: emailSendCommand,
|
|
1897
|
+
write: true
|
|
1898
|
+
});
|
|
1899
|
+
|
|
1900
|
+
// src/commands/builtin/email/stat.ts
|
|
1901
|
+
import {
|
|
1902
|
+
FileType as FileType3,
|
|
1903
|
+
IOResult as IOResult17,
|
|
1904
|
+
ResourceName as ResourceName17,
|
|
1905
|
+
command as command17,
|
|
1906
|
+
specOf as specOf12
|
|
1907
|
+
} from "@struktoai/mirage-core";
|
|
1908
|
+
var ENC18 = new TextEncoder();
|
|
1909
|
+
var TYPE_LABELS = {
|
|
1910
|
+
[FileType3.DIRECTORY]: "directory",
|
|
1911
|
+
[FileType3.TEXT]: "regular file",
|
|
1912
|
+
[FileType3.BINARY]: "regular file",
|
|
1913
|
+
[FileType3.JSON]: "regular file",
|
|
1914
|
+
[FileType3.CSV]: "regular file"
|
|
1915
|
+
};
|
|
1916
|
+
function formatStat(fmt, s) {
|
|
1917
|
+
return fmt.replace(/%(.)/g, (_, spec) => {
|
|
1918
|
+
if (spec === "n") return s.name;
|
|
1919
|
+
if (spec === "s") return String(s.size ?? 0);
|
|
1920
|
+
if (spec === "F")
|
|
1921
|
+
return s.type !== null ? TYPE_LABELS[s.type] ?? "regular file" : "regular file";
|
|
1922
|
+
if (spec === "y") return s.modified ?? "";
|
|
1923
|
+
return "?";
|
|
1924
|
+
});
|
|
1925
|
+
}
|
|
1926
|
+
async function statCommand(accessor, paths, _texts, opts) {
|
|
1927
|
+
if (paths.length === 0) {
|
|
1928
|
+
return [null, new IOResult17({ exitCode: 1, stderr: ENC18.encode("stat: missing operand\n") })];
|
|
1929
|
+
}
|
|
1930
|
+
const resolved = await resolveGlob(accessor, paths, opts.index ?? void 0);
|
|
1931
|
+
const fmt = typeof opts.flags.c === "string" ? opts.flags.c : typeof opts.flags.f === "string" ? opts.flags.f : null;
|
|
1932
|
+
const lines = [];
|
|
1933
|
+
for (const p of resolved) {
|
|
1934
|
+
const s = await stat(accessor, p, opts.index ?? void 0);
|
|
1935
|
+
if (fmt !== null) {
|
|
1936
|
+
lines.push(formatStat(fmt, s));
|
|
1937
|
+
} else {
|
|
1938
|
+
const sizeStr = s.size === null ? "None" : String(s.size);
|
|
1939
|
+
const modStr = s.modified ?? "None";
|
|
1940
|
+
const typeStr = s.type ?? "None";
|
|
1941
|
+
lines.push(`name=${s.name} size=${sizeStr} modified=${modStr} type=${typeStr}`);
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
const out = ENC18.encode(lines.join("\n"));
|
|
1945
|
+
return [out, new IOResult17()];
|
|
1946
|
+
}
|
|
1947
|
+
var EMAIL_STAT = command17({
|
|
1948
|
+
name: "stat",
|
|
1949
|
+
resource: ResourceName17.EMAIL,
|
|
1950
|
+
spec: specOf12("stat"),
|
|
1951
|
+
fn: statCommand,
|
|
1952
|
+
provision: metadataProvision
|
|
1953
|
+
});
|
|
1954
|
+
|
|
1955
|
+
// src/commands/builtin/email/tail.ts
|
|
1956
|
+
import {
|
|
1957
|
+
IOResult as IOResult18,
|
|
1958
|
+
ResourceName as ResourceName18,
|
|
1959
|
+
command as command18,
|
|
1960
|
+
parseN,
|
|
1961
|
+
readStdinAsync as readStdinAsync5,
|
|
1962
|
+
specOf as specOf13,
|
|
1963
|
+
tailBytes
|
|
1964
|
+
} from "@struktoai/mirage-core";
|
|
1965
|
+
var ENC19 = new TextEncoder();
|
|
1966
|
+
async function tailCommand(accessor, paths, _texts, opts) {
|
|
1967
|
+
const [lines, plusMode] = parseN(typeof opts.flags.n === "string" ? opts.flags.n : null);
|
|
1968
|
+
const bytesMode = typeof opts.flags.c === "string" ? Number.parseInt(opts.flags.c, 10) : null;
|
|
1969
|
+
if (paths.length > 0) {
|
|
1970
|
+
const resolved = await resolveGlob(accessor, paths, opts.index ?? void 0);
|
|
1971
|
+
const first = resolved[0];
|
|
1972
|
+
if (first === void 0) return [null, new IOResult18()];
|
|
1973
|
+
const raw2 = await read(accessor, first, opts.index ?? void 0);
|
|
1974
|
+
const out2 = tailBytes(raw2, lines, bytesMode, plusMode);
|
|
1975
|
+
return [out2, new IOResult18()];
|
|
1976
|
+
}
|
|
1977
|
+
const raw = await readStdinAsync5(opts.stdin);
|
|
1978
|
+
if (raw === null) {
|
|
1979
|
+
return [null, new IOResult18({ exitCode: 1, stderr: ENC19.encode("tail: missing operand\n") })];
|
|
1980
|
+
}
|
|
1981
|
+
const out = tailBytes(raw, lines, bytesMode, plusMode);
|
|
1982
|
+
return [out, new IOResult18()];
|
|
1983
|
+
}
|
|
1984
|
+
var EMAIL_TAIL = command18({
|
|
1985
|
+
name: "tail",
|
|
1986
|
+
resource: ResourceName18.EMAIL,
|
|
1987
|
+
spec: specOf13("tail"),
|
|
1988
|
+
fn: tailCommand,
|
|
1989
|
+
provision: fileReadProvision
|
|
1990
|
+
});
|
|
1991
|
+
|
|
1992
|
+
// src/commands/builtin/email/tree.ts
|
|
1993
|
+
import {
|
|
1994
|
+
FileType as FileType4,
|
|
1995
|
+
IOResult as IOResult19,
|
|
1996
|
+
PathSpec as PathSpec6,
|
|
1997
|
+
ResourceName as ResourceName19,
|
|
1998
|
+
command as command19,
|
|
1999
|
+
specOf as specOf14
|
|
2000
|
+
} from "@struktoai/mirage-core";
|
|
2001
|
+
var ENC20 = new TextEncoder();
|
|
2002
|
+
function fnmatch3(name, pattern) {
|
|
2003
|
+
const re = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\?/g, ".").replace(/\*/g, ".*");
|
|
2004
|
+
return new RegExp(`^${re}$`).test(name);
|
|
2005
|
+
}
|
|
2006
|
+
async function treeRecurse(accessor, path, prefix, depth, treeOpts, warnings, indexCache) {
|
|
2007
|
+
const lines = [];
|
|
2008
|
+
let entries;
|
|
2009
|
+
try {
|
|
2010
|
+
entries = await readdir(accessor, path, indexCache ?? void 0);
|
|
2011
|
+
} catch (err) {
|
|
2012
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2013
|
+
warnings.push(`tree: '${path.original}': ${msg}`);
|
|
2014
|
+
return lines;
|
|
2015
|
+
}
|
|
2016
|
+
const filtered = [];
|
|
2017
|
+
for (const entry of entries) {
|
|
2018
|
+
try {
|
|
2019
|
+
const entrySpec = new PathSpec6({
|
|
2020
|
+
original: entry,
|
|
2021
|
+
directory: entry,
|
|
2022
|
+
resolved: false,
|
|
2023
|
+
prefix: path.prefix
|
|
2024
|
+
});
|
|
2025
|
+
const s = await stat(accessor, entrySpec, indexCache ?? void 0);
|
|
2026
|
+
if (!treeOpts.showHidden && s.name.startsWith(".")) continue;
|
|
2027
|
+
if (treeOpts.ignorePattern !== null && fnmatch3(s.name, treeOpts.ignorePattern)) continue;
|
|
2028
|
+
if (treeOpts.dirsOnly && s.type !== FileType4.DIRECTORY) continue;
|
|
2029
|
+
const notDir = s.type !== FileType4.DIRECTORY;
|
|
2030
|
+
if (treeOpts.matchPattern !== null && notDir && !fnmatch3(s.name, treeOpts.matchPattern))
|
|
2031
|
+
continue;
|
|
2032
|
+
filtered.push([entrySpec, s]);
|
|
2033
|
+
} catch (err) {
|
|
2034
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2035
|
+
warnings.push(`tree: '${entry}': ${msg}`);
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
for (let i = 0; i < filtered.length; i++) {
|
|
2039
|
+
const pair = filtered[i];
|
|
2040
|
+
if (pair === void 0) continue;
|
|
2041
|
+
const [entrySpec, s] = pair;
|
|
2042
|
+
const isLast = i === filtered.length - 1;
|
|
2043
|
+
const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
2044
|
+
lines.push(prefix + connector + s.name);
|
|
2045
|
+
if (s.type === FileType4.DIRECTORY) {
|
|
2046
|
+
if (treeOpts.maxDepth !== null && depth >= treeOpts.maxDepth) continue;
|
|
2047
|
+
const extension = isLast ? " " : "\u2502 ";
|
|
2048
|
+
const sub = await treeRecurse(
|
|
2049
|
+
accessor,
|
|
2050
|
+
entrySpec,
|
|
2051
|
+
prefix + extension,
|
|
2052
|
+
depth + 1,
|
|
2053
|
+
treeOpts,
|
|
2054
|
+
warnings,
|
|
2055
|
+
indexCache
|
|
2056
|
+
);
|
|
2057
|
+
lines.push(...sub);
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
return lines;
|
|
2061
|
+
}
|
|
2062
|
+
async function treeCommand(accessor, paths, _texts, opts) {
|
|
2063
|
+
const resolved = await resolveGlob(accessor, paths, opts.index ?? void 0);
|
|
2064
|
+
const p0 = resolved[0] ?? new PathSpec6({
|
|
2065
|
+
original: opts.cwd,
|
|
2066
|
+
directory: opts.cwd,
|
|
2067
|
+
resolved: false,
|
|
2068
|
+
prefix: opts.mountPrefix ?? ""
|
|
2069
|
+
});
|
|
2070
|
+
const maxDepth = typeof opts.flags.L === "string" ? Number.parseInt(opts.flags.L, 10) : null;
|
|
2071
|
+
const treeOpts = {
|
|
2072
|
+
maxDepth,
|
|
2073
|
+
showHidden: opts.flags.a === true,
|
|
2074
|
+
ignorePattern: typeof opts.flags.args_I === "string" ? opts.flags.args_I : null,
|
|
2075
|
+
dirsOnly: opts.flags.d === true,
|
|
2076
|
+
matchPattern: typeof opts.flags.P === "string" ? opts.flags.P : null
|
|
2077
|
+
};
|
|
2078
|
+
const warnings = [];
|
|
2079
|
+
const results = await treeRecurse(accessor, p0, "", 0, treeOpts, warnings, opts.index);
|
|
2080
|
+
const out = ENC20.encode(results.join("\n"));
|
|
2081
|
+
const stderr = warnings.length > 0 ? ENC20.encode(warnings.join("\n")) : null;
|
|
2082
|
+
return [out, new IOResult19({ stderr })];
|
|
2083
|
+
}
|
|
2084
|
+
var EMAIL_TREE = command19({
|
|
2085
|
+
name: "tree",
|
|
2086
|
+
resource: ResourceName19.EMAIL,
|
|
2087
|
+
spec: specOf14("tree"),
|
|
2088
|
+
fn: treeCommand
|
|
2089
|
+
});
|
|
2090
|
+
|
|
2091
|
+
// src/commands/builtin/email/email_triage.ts
|
|
2092
|
+
import {
|
|
2093
|
+
CommandSpec as CommandSpec6,
|
|
2094
|
+
IOResult as IOResult20,
|
|
2095
|
+
OperandKind as OperandKind6,
|
|
2096
|
+
Option as Option6,
|
|
2097
|
+
ResourceName as ResourceName20,
|
|
2098
|
+
command as command20
|
|
2099
|
+
} from "@struktoai/mirage-core";
|
|
2100
|
+
var ENC21 = new TextEncoder();
|
|
2101
|
+
var SPEC6 = new CommandSpec6({
|
|
2102
|
+
options: [
|
|
2103
|
+
new Option6({ long: "--folder", valueKind: OperandKind6.TEXT }),
|
|
2104
|
+
new Option6({ long: "--max", valueKind: OperandKind6.TEXT }),
|
|
2105
|
+
new Option6({ long: "--unseen" }),
|
|
2106
|
+
new Option6({ long: "--subject", valueKind: OperandKind6.TEXT }),
|
|
2107
|
+
new Option6({ long: "--from", valueKind: OperandKind6.TEXT }),
|
|
2108
|
+
new Option6({ long: "--to", valueKind: OperandKind6.TEXT }),
|
|
2109
|
+
new Option6({ long: "--body", valueKind: OperandKind6.TEXT }),
|
|
2110
|
+
new Option6({ long: "--since", valueKind: OperandKind6.TEXT }),
|
|
2111
|
+
new Option6({ long: "--before", valueKind: OperandKind6.TEXT })
|
|
2112
|
+
]
|
|
2113
|
+
});
|
|
2114
|
+
async function emailTriageCommand(accessor, _paths, _texts, opts) {
|
|
2115
|
+
const folder = typeof opts.flags.folder === "string" ? opts.flags.folder : "INBOX";
|
|
2116
|
+
const maxResults = typeof opts.flags.max === "string" ? Number.parseInt(opts.flags.max, 10) : 20;
|
|
2117
|
+
const uids = await searchMessages(
|
|
2118
|
+
accessor,
|
|
2119
|
+
folder,
|
|
2120
|
+
{
|
|
2121
|
+
text: typeof opts.flags.body === "string" ? opts.flags.body : null,
|
|
2122
|
+
subject: typeof opts.flags.subject === "string" ? opts.flags.subject : null,
|
|
2123
|
+
fromAddr: typeof opts.flags.from === "string" ? opts.flags.from : null,
|
|
2124
|
+
toAddr: typeof opts.flags.to === "string" ? opts.flags.to : null,
|
|
2125
|
+
since: typeof opts.flags.since === "string" ? opts.flags.since : null,
|
|
2126
|
+
before: typeof opts.flags.before === "string" ? opts.flags.before : null,
|
|
2127
|
+
unseen: opts.flags.unseen === true
|
|
2128
|
+
},
|
|
2129
|
+
maxResults
|
|
2130
|
+
);
|
|
2131
|
+
if (uids.length === 0) {
|
|
2132
|
+
const out2 = ENC21.encode("[]");
|
|
2133
|
+
return [out2, new IOResult20()];
|
|
2134
|
+
}
|
|
2135
|
+
const headers = await fetchHeaders(accessor, folder, uids);
|
|
2136
|
+
const out = ENC21.encode(JSON.stringify(headers));
|
|
2137
|
+
return [out, new IOResult20()];
|
|
2138
|
+
}
|
|
2139
|
+
var EMAIL_TRIAGE = command20({
|
|
2140
|
+
name: "email-triage",
|
|
2141
|
+
resource: ResourceName20.EMAIL,
|
|
2142
|
+
spec: SPEC6,
|
|
2143
|
+
fn: emailTriageCommand
|
|
2144
|
+
});
|
|
2145
|
+
|
|
2146
|
+
// src/commands/builtin/email/wc.ts
|
|
2147
|
+
import {
|
|
2148
|
+
IOResult as IOResult21,
|
|
2149
|
+
ResourceName as ResourceName21,
|
|
2150
|
+
command as command21,
|
|
2151
|
+
readStdinAsync as readStdinAsync6,
|
|
2152
|
+
specOf as specOf15
|
|
2153
|
+
} from "@struktoai/mirage-core";
|
|
2154
|
+
var ENC22 = new TextEncoder();
|
|
2155
|
+
var DEC4 = new TextDecoder("utf-8", { fatal: false });
|
|
2156
|
+
function countLines(text) {
|
|
2157
|
+
let n = 0;
|
|
2158
|
+
for (let i = 0; i < text.length; i++) if (text.charCodeAt(i) === 10) n += 1;
|
|
2159
|
+
return n;
|
|
2160
|
+
}
|
|
2161
|
+
function countWords(text) {
|
|
2162
|
+
const trimmed = text.trim();
|
|
2163
|
+
if (trimmed === "") return 0;
|
|
2164
|
+
return trimmed.split(/\s+/).length;
|
|
2165
|
+
}
|
|
2166
|
+
function maxLineLen(text) {
|
|
2167
|
+
let max = 0;
|
|
2168
|
+
for (const line of text.split("\n")) if (line.length > max) max = line.length;
|
|
2169
|
+
return max;
|
|
2170
|
+
}
|
|
2171
|
+
async function wcCommand(accessor, paths, _texts, opts) {
|
|
2172
|
+
let data = null;
|
|
2173
|
+
if (paths.length > 0) {
|
|
2174
|
+
const resolved = await resolveGlob(accessor, paths, opts.index ?? void 0);
|
|
2175
|
+
const first = resolved[0];
|
|
2176
|
+
if (first === void 0) return [null, new IOResult21()];
|
|
2177
|
+
data = await read(accessor, first, opts.index ?? void 0);
|
|
2178
|
+
} else {
|
|
2179
|
+
data = await readStdinAsync6(opts.stdin);
|
|
2180
|
+
if (data === null) {
|
|
2181
|
+
return [null, new IOResult21({ exitCode: 1, stderr: ENC22.encode("wc: missing operand\n") })];
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
const text = DEC4.decode(data);
|
|
2185
|
+
const lineCount = countLines(text);
|
|
2186
|
+
const wordCount = countWords(text);
|
|
2187
|
+
const byteCount = data.byteLength;
|
|
2188
|
+
if (opts.flags.L === true) {
|
|
2189
|
+
const out2 = ENC22.encode(String(maxLineLen(text)));
|
|
2190
|
+
return [out2, new IOResult21()];
|
|
2191
|
+
}
|
|
2192
|
+
if (opts.flags.args_l === true) return [ENC22.encode(String(lineCount)), new IOResult21()];
|
|
2193
|
+
if (opts.flags.w === true) return [ENC22.encode(String(wordCount)), new IOResult21()];
|
|
2194
|
+
if (opts.flags.m === true) return [ENC22.encode(String(text.length)), new IOResult21()];
|
|
2195
|
+
if (opts.flags.c === true) return [ENC22.encode(String(byteCount)), new IOResult21()];
|
|
2196
|
+
const out = ENC22.encode(
|
|
2197
|
+
`${String(lineCount)} ${String(wordCount)} ${String(byteCount)}`
|
|
2198
|
+
);
|
|
2199
|
+
return [out, new IOResult21()];
|
|
2200
|
+
}
|
|
2201
|
+
var EMAIL_WC = command21({
|
|
2202
|
+
name: "wc",
|
|
2203
|
+
resource: ResourceName21.EMAIL,
|
|
2204
|
+
spec: specOf15("wc"),
|
|
2205
|
+
fn: wcCommand,
|
|
2206
|
+
provision: fileReadProvision
|
|
2207
|
+
});
|
|
2208
|
+
|
|
2209
|
+
// src/commands/builtin/email/index.ts
|
|
2210
|
+
var EMAIL_COMMANDS = [
|
|
2211
|
+
...EMAIL_BASENAME,
|
|
2212
|
+
...EMAIL_CAT,
|
|
2213
|
+
...EMAIL_DIRNAME,
|
|
2214
|
+
...EMAIL_FIND,
|
|
2215
|
+
...EMAIL_GREP,
|
|
2216
|
+
...EMAIL_HEAD,
|
|
2217
|
+
...EMAIL_JQ,
|
|
2218
|
+
...EMAIL_LS,
|
|
2219
|
+
...EMAIL_NL,
|
|
2220
|
+
...EMAIL_REALPATH,
|
|
2221
|
+
...EMAIL_RG,
|
|
2222
|
+
...EMAIL_STAT,
|
|
2223
|
+
...EMAIL_TAIL,
|
|
2224
|
+
...EMAIL_TREE,
|
|
2225
|
+
...EMAIL_WC,
|
|
2226
|
+
...EMAIL_SEND,
|
|
2227
|
+
...EMAIL_REPLY,
|
|
2228
|
+
...EMAIL_REPLY_ALL,
|
|
2229
|
+
...EMAIL_FORWARD,
|
|
2230
|
+
...EMAIL_TRIAGE,
|
|
2231
|
+
...EMAIL_READ
|
|
2232
|
+
];
|
|
2233
|
+
|
|
2234
|
+
// src/ops/email/read.ts
|
|
2235
|
+
import { ResourceName as ResourceName22 } from "@struktoai/mirage-core";
|
|
2236
|
+
var readOp = {
|
|
2237
|
+
name: "read",
|
|
2238
|
+
resource: ResourceName22.EMAIL,
|
|
2239
|
+
filetype: null,
|
|
2240
|
+
write: false,
|
|
2241
|
+
fn: (accessor, path, _args, kwargs) => {
|
|
2242
|
+
return read(accessor, path, kwargs.index);
|
|
2243
|
+
}
|
|
2244
|
+
};
|
|
2245
|
+
|
|
2246
|
+
// src/ops/email/readdir.ts
|
|
2247
|
+
import { ResourceName as ResourceName23 } from "@struktoai/mirage-core";
|
|
2248
|
+
var readdirOp = {
|
|
2249
|
+
name: "readdir",
|
|
2250
|
+
resource: ResourceName23.EMAIL,
|
|
2251
|
+
filetype: null,
|
|
2252
|
+
write: false,
|
|
2253
|
+
fn: (accessor, path, _args, kwargs) => {
|
|
2254
|
+
return readdir(accessor, path, kwargs.index);
|
|
2255
|
+
}
|
|
2256
|
+
};
|
|
2257
|
+
|
|
2258
|
+
// src/ops/email/stat.ts
|
|
2259
|
+
import { ResourceName as ResourceName24 } from "@struktoai/mirage-core";
|
|
2260
|
+
var statOp = {
|
|
2261
|
+
name: "stat",
|
|
2262
|
+
resource: ResourceName24.EMAIL,
|
|
2263
|
+
filetype: null,
|
|
2264
|
+
write: false,
|
|
2265
|
+
fn: (accessor, path, _args, kwargs) => {
|
|
2266
|
+
return stat(accessor, path, kwargs.index);
|
|
2267
|
+
}
|
|
2268
|
+
};
|
|
2269
|
+
|
|
2270
|
+
// src/ops/email/index.ts
|
|
2271
|
+
var EMAIL_OPS = [readdirOp, readOp, statOp];
|
|
2272
|
+
|
|
2273
|
+
// src/resource/email/prompt.ts
|
|
2274
|
+
var EMAIL_PROMPT = `{prefix}
|
|
2275
|
+
<folder>/
|
|
2276
|
+
<yyyy-mm-dd>/
|
|
2277
|
+
<subject>__<uid>.email.json
|
|
2278
|
+
<subject>__<uid>/ # if attachments exist
|
|
2279
|
+
<attachment-filename>
|
|
2280
|
+
Folders include: INBOX, Sent, Drafts, etc. cat shows email as JSON.`;
|
|
2281
|
+
var EMAIL_WRITE_PROMPT = ` Write commands:
|
|
2282
|
+
email-send "to@email.com" "subject" "body"
|
|
2283
|
+
email-reply <email-path> "reply body"
|
|
2284
|
+
email-forward <email-path> "to@email.com"`;
|
|
2285
|
+
|
|
2286
|
+
// src/resource/email/email.ts
|
|
2287
|
+
var EmailResource = class {
|
|
2288
|
+
kind = ResourceName25.EMAIL;
|
|
2289
|
+
isRemote = true;
|
|
2290
|
+
indexTtl = 86400;
|
|
2291
|
+
prompt = EMAIL_PROMPT;
|
|
2292
|
+
config;
|
|
2293
|
+
accessor;
|
|
2294
|
+
index;
|
|
2295
|
+
constructor(config) {
|
|
2296
|
+
this.config = config;
|
|
2297
|
+
this.accessor = new EmailAccessor(config);
|
|
2298
|
+
this.index = new RAMIndexCacheStore({ ttl: 86400 });
|
|
2299
|
+
}
|
|
2300
|
+
open() {
|
|
2301
|
+
return Promise.resolve();
|
|
2302
|
+
}
|
|
2303
|
+
async close() {
|
|
2304
|
+
await this.accessor.close();
|
|
2305
|
+
}
|
|
2306
|
+
commands() {
|
|
2307
|
+
return EMAIL_COMMANDS;
|
|
2308
|
+
}
|
|
2309
|
+
ops() {
|
|
2310
|
+
return EMAIL_OPS;
|
|
2311
|
+
}
|
|
2312
|
+
readFile(p) {
|
|
2313
|
+
return read(this.accessor, p, this.index);
|
|
2314
|
+
}
|
|
2315
|
+
readdir(p) {
|
|
2316
|
+
return readdir(this.accessor, p, this.index);
|
|
2317
|
+
}
|
|
2318
|
+
stat(p) {
|
|
2319
|
+
return stat(this.accessor, p, this.index);
|
|
2320
|
+
}
|
|
2321
|
+
async fingerprint(p) {
|
|
2322
|
+
const lookup = await this.index.get(p.original);
|
|
2323
|
+
return lookup.entry?.remoteTime ?? null;
|
|
2324
|
+
}
|
|
2325
|
+
glob(paths, prefix = "") {
|
|
2326
|
+
const effective = prefix !== "" ? paths.map(
|
|
2327
|
+
(p) => p.prefix !== "" ? p : new PathSpec7({
|
|
2328
|
+
original: p.original,
|
|
2329
|
+
directory: p.directory,
|
|
2330
|
+
...p.pattern !== null ? { pattern: p.pattern } : {},
|
|
2331
|
+
resolved: p.resolved,
|
|
2332
|
+
prefix
|
|
2333
|
+
})
|
|
2334
|
+
) : paths;
|
|
2335
|
+
return resolveGlob(this.accessor, effective, this.index);
|
|
2336
|
+
}
|
|
2337
|
+
getState() {
|
|
2338
|
+
return Promise.resolve({
|
|
2339
|
+
type: this.kind,
|
|
2340
|
+
needsOverride: true,
|
|
2341
|
+
redactedFields: ["password"],
|
|
2342
|
+
config: redactEmailConfig(this.config)
|
|
2343
|
+
});
|
|
2344
|
+
}
|
|
2345
|
+
loadState(_state) {
|
|
2346
|
+
return Promise.resolve();
|
|
2347
|
+
}
|
|
2348
|
+
};
|
|
2349
|
+
|
|
2350
|
+
export {
|
|
2351
|
+
EmailAccessor,
|
|
2352
|
+
EMAIL_COMMANDS,
|
|
2353
|
+
EMAIL_OPS,
|
|
2354
|
+
EMAIL_PROMPT,
|
|
2355
|
+
EMAIL_WRITE_PROMPT,
|
|
2356
|
+
EmailResource
|
|
2357
|
+
};
|
|
2358
|
+
//# sourceMappingURL=chunk-OYJXYJTE.js.map
|