@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.
Files changed (191) hide show
  1. package/README.md +230 -0
  2. package/dist/box-4P7DCJH7.js +8 -0
  3. package/dist/box-4P7DCJH7.js.map +1 -0
  4. package/dist/chunk-35PVZRWU.js +24 -0
  5. package/dist/chunk-35PVZRWU.js.map +1 -0
  6. package/dist/chunk-3PBSL7BS.js +93 -0
  7. package/dist/chunk-3PBSL7BS.js.map +1 -0
  8. package/dist/chunk-3Y625AUP.js +24 -0
  9. package/dist/chunk-3Y625AUP.js.map +1 -0
  10. package/dist/chunk-5E6BULAK.js +92 -0
  11. package/dist/chunk-5E6BULAK.js.map +1 -0
  12. package/dist/chunk-5ITBZN55.js +24 -0
  13. package/dist/chunk-5ITBZN55.js.map +1 -0
  14. package/dist/chunk-6M2NL363.js +94 -0
  15. package/dist/chunk-6M2NL363.js.map +1 -0
  16. package/dist/chunk-6M3QVVOH.js +32 -0
  17. package/dist/chunk-6M3QVVOH.js.map +1 -0
  18. package/dist/chunk-6VKNSO6U.js +56 -0
  19. package/dist/chunk-6VKNSO6U.js.map +1 -0
  20. package/dist/chunk-6Y6GWMFF.js +24 -0
  21. package/dist/chunk-6Y6GWMFF.js.map +1 -0
  22. package/dist/chunk-7QXJ4KWQ.js +132 -0
  23. package/dist/chunk-7QXJ4KWQ.js.map +1 -0
  24. package/dist/chunk-ALAMUIGJ.js +57 -0
  25. package/dist/chunk-ALAMUIGJ.js.map +1 -0
  26. package/dist/chunk-DCJQ2PIX.js +100 -0
  27. package/dist/chunk-DCJQ2PIX.js.map +1 -0
  28. package/dist/chunk-EKPH4MMD.js +27 -0
  29. package/dist/chunk-EKPH4MMD.js.map +1 -0
  30. package/dist/chunk-FZ2GJ4BJ.js +29 -0
  31. package/dist/chunk-FZ2GJ4BJ.js.map +1 -0
  32. package/dist/chunk-G7O6DAWU.js +178 -0
  33. package/dist/chunk-G7O6DAWU.js.map +1 -0
  34. package/dist/chunk-GCB4JUF7.js +94 -0
  35. package/dist/chunk-GCB4JUF7.js.map +1 -0
  36. package/dist/chunk-GKQNMLQU.js +7757 -0
  37. package/dist/chunk-GKQNMLQU.js.map +1 -0
  38. package/dist/chunk-GO67AOYK.js +24 -0
  39. package/dist/chunk-GO67AOYK.js.map +1 -0
  40. package/dist/chunk-GPF66TH2.js +24 -0
  41. package/dist/chunk-GPF66TH2.js.map +1 -0
  42. package/dist/chunk-H3JR7MN3.js +45 -0
  43. package/dist/chunk-H3JR7MN3.js.map +1 -0
  44. package/dist/chunk-H74QYTN7.js +50 -0
  45. package/dist/chunk-H74QYTN7.js.map +1 -0
  46. package/dist/chunk-HEQL3LPK.js +66 -0
  47. package/dist/chunk-HEQL3LPK.js.map +1 -0
  48. package/dist/chunk-HXWGG4K7.js +66 -0
  49. package/dist/chunk-HXWGG4K7.js.map +1 -0
  50. package/dist/chunk-I4RRGZGB.js +24 -0
  51. package/dist/chunk-I4RRGZGB.js.map +1 -0
  52. package/dist/chunk-ICSS5ETT.js +67 -0
  53. package/dist/chunk-ICSS5ETT.js.map +1 -0
  54. package/dist/chunk-KINMBZHC.js +18 -0
  55. package/dist/chunk-KINMBZHC.js.map +1 -0
  56. package/dist/chunk-LBBM3LKK.js +99 -0
  57. package/dist/chunk-LBBM3LKK.js.map +1 -0
  58. package/dist/chunk-LPMPTQYP.js +36 -0
  59. package/dist/chunk-LPMPTQYP.js.map +1 -0
  60. package/dist/chunk-M42JTWPR.js +2794 -0
  61. package/dist/chunk-M42JTWPR.js.map +1 -0
  62. package/dist/chunk-MVE5VVMQ.js +91 -0
  63. package/dist/chunk-MVE5VVMQ.js.map +1 -0
  64. package/dist/chunk-MY3NUJJD.js +67 -0
  65. package/dist/chunk-MY3NUJJD.js.map +1 -0
  66. package/dist/chunk-NOZZFJEB.js +93 -0
  67. package/dist/chunk-NOZZFJEB.js.map +1 -0
  68. package/dist/chunk-OYJXYJTE.js +2358 -0
  69. package/dist/chunk-OYJXYJTE.js.map +1 -0
  70. package/dist/chunk-PEZYBKZH.js +124 -0
  71. package/dist/chunk-PEZYBKZH.js.map +1 -0
  72. package/dist/chunk-QWWOBAYU.js +26 -0
  73. package/dist/chunk-QWWOBAYU.js.map +1 -0
  74. package/dist/chunk-RQYYYBQG.js +24 -0
  75. package/dist/chunk-RQYYYBQG.js.map +1 -0
  76. package/dist/chunk-T2WWAEKH.js +7591 -0
  77. package/dist/chunk-T2WWAEKH.js.map +1 -0
  78. package/dist/chunk-U6QSXTUS.js +7 -0
  79. package/dist/chunk-U6QSXTUS.js.map +1 -0
  80. package/dist/chunk-UWMOTOXA.js +93 -0
  81. package/dist/chunk-UWMOTOXA.js.map +1 -0
  82. package/dist/chunk-UZZD4MRJ.js +95 -0
  83. package/dist/chunk-UZZD4MRJ.js.map +1 -0
  84. package/dist/chunk-V5OLWE7F.js +88 -0
  85. package/dist/chunk-V5OLWE7F.js.map +1 -0
  86. package/dist/chunk-VCLMTMSO.js +23 -0
  87. package/dist/chunk-VCLMTMSO.js.map +1 -0
  88. package/dist/chunk-WSDQ2MRG.js +102 -0
  89. package/dist/chunk-WSDQ2MRG.js.map +1 -0
  90. package/dist/chunk-XDEEOTVK.js +93 -0
  91. package/dist/chunk-XDEEOTVK.js.map +1 -0
  92. package/dist/chunk-XQUQSNCA.js +117 -0
  93. package/dist/chunk-XQUQSNCA.js.map +1 -0
  94. package/dist/chunk-XVYZO2FG.js +14 -0
  95. package/dist/chunk-XVYZO2FG.js.map +1 -0
  96. package/dist/chunk-ZC4KNEOQ.js +50 -0
  97. package/dist/chunk-ZC4KNEOQ.js.map +1 -0
  98. package/dist/config-5ZPGISS6.js +13 -0
  99. package/dist/config-5ZPGISS6.js.map +1 -0
  100. package/dist/config-6HWSKHEU.js +9 -0
  101. package/dist/config-6HWSKHEU.js.map +1 -0
  102. package/dist/config-6TNFPYZF.js +9 -0
  103. package/dist/config-6TNFPYZF.js.map +1 -0
  104. package/dist/config-BHLXQ3OL.js +9 -0
  105. package/dist/config-BHLXQ3OL.js.map +1 -0
  106. package/dist/config-E7UOEWEP.js +9 -0
  107. package/dist/config-E7UOEWEP.js.map +1 -0
  108. package/dist/config-FNR6LJ5H.js +11 -0
  109. package/dist/config-FNR6LJ5H.js.map +1 -0
  110. package/dist/config-HIZHA5VD.js +13 -0
  111. package/dist/config-HIZHA5VD.js.map +1 -0
  112. package/dist/config-HNSDNQZH.js +9 -0
  113. package/dist/config-HNSDNQZH.js.map +1 -0
  114. package/dist/config-IXLFUSMQ.js +9 -0
  115. package/dist/config-IXLFUSMQ.js.map +1 -0
  116. package/dist/config-J3TM7NIS.js +9 -0
  117. package/dist/config-J3TM7NIS.js.map +1 -0
  118. package/dist/config-JXLZ4XEW.js +9 -0
  119. package/dist/config-JXLZ4XEW.js.map +1 -0
  120. package/dist/config-KQCEMLNZ.js +9 -0
  121. package/dist/config-KQCEMLNZ.js.map +1 -0
  122. package/dist/config-MOLWOSRG.js +9 -0
  123. package/dist/config-MOLWOSRG.js.map +1 -0
  124. package/dist/config-R3XJ5OW4.js +9 -0
  125. package/dist/config-R3XJ5OW4.js.map +1 -0
  126. package/dist/config-S47UDVRZ.js +13 -0
  127. package/dist/config-S47UDVRZ.js.map +1 -0
  128. package/dist/config-UKZ72IDO.js +13 -0
  129. package/dist/config-UKZ72IDO.js.map +1 -0
  130. package/dist/config-V7XI5UTG.js +9 -0
  131. package/dist/config-V7XI5UTG.js.map +1 -0
  132. package/dist/config-YLIX3ZBT.js +9 -0
  133. package/dist/config-YLIX3ZBT.js.map +1 -0
  134. package/dist/config-Z5JGDOKE.js +9 -0
  135. package/dist/config-Z5JGDOKE.js.map +1 -0
  136. package/dist/config-ZCZZAH5W.js +9 -0
  137. package/dist/config-ZCZZAH5W.js.map +1 -0
  138. package/dist/config-ZZGIWDUC.js +9 -0
  139. package/dist/config-ZZGIWDUC.js.map +1 -0
  140. package/dist/discord-2PWP2MGX.js +8 -0
  141. package/dist/discord-2PWP2MGX.js.map +1 -0
  142. package/dist/disk-C7TJNU2B.js +8 -0
  143. package/dist/disk-C7TJNU2B.js.map +1 -0
  144. package/dist/dropbox-L5DPYGKR.js +8 -0
  145. package/dist/dropbox-L5DPYGKR.js.map +1 -0
  146. package/dist/email-HASRFBKC.js +8 -0
  147. package/dist/email-HASRFBKC.js.map +1 -0
  148. package/dist/gcs-P5QMKTPR.js +10 -0
  149. package/dist/gcs-P5QMKTPR.js.map +1 -0
  150. package/dist/gdocs-IF2I35HZ.js +8 -0
  151. package/dist/gdocs-IF2I35HZ.js.map +1 -0
  152. package/dist/gdrive-TAETVCKP.js +8 -0
  153. package/dist/gdrive-TAETVCKP.js.map +1 -0
  154. package/dist/github-5E6GSCNZ.js +8 -0
  155. package/dist/github-5E6GSCNZ.js.map +1 -0
  156. package/dist/github_ci-2DSZI3Y7.js +8 -0
  157. package/dist/github_ci-2DSZI3Y7.js.map +1 -0
  158. package/dist/gmail-F5VYGKOT.js +8 -0
  159. package/dist/gmail-F5VYGKOT.js.map +1 -0
  160. package/dist/gsheets-BAO54CQ7.js +8 -0
  161. package/dist/gsheets-BAO54CQ7.js.map +1 -0
  162. package/dist/gslides-A62CEEMR.js +8 -0
  163. package/dist/gslides-A62CEEMR.js.map +1 -0
  164. package/dist/index.d.ts +1671 -0
  165. package/dist/index.js +3564 -0
  166. package/dist/index.js.map +1 -0
  167. package/dist/langfuse-YKBYRQLU.js +8 -0
  168. package/dist/langfuse-YKBYRQLU.js.map +1 -0
  169. package/dist/linear-WJDQ6JDE.js +8 -0
  170. package/dist/linear-WJDQ6JDE.js.map +1 -0
  171. package/dist/mongodb-MK4DVSIK.js +7 -0
  172. package/dist/mongodb-MK4DVSIK.js.map +1 -0
  173. package/dist/oci-KZ7XFZ2X.js +10 -0
  174. package/dist/oci-KZ7XFZ2X.js.map +1 -0
  175. package/dist/postgres-M2N2APFZ.js +7 -0
  176. package/dist/postgres-M2N2APFZ.js.map +1 -0
  177. package/dist/r2-X4DDNSAZ.js +10 -0
  178. package/dist/r2-X4DDNSAZ.js.map +1 -0
  179. package/dist/redis-NVCXUUIO.js +7 -0
  180. package/dist/redis-NVCXUUIO.js.map +1 -0
  181. package/dist/s3-HVAL7ZX3.js +8 -0
  182. package/dist/s3-HVAL7ZX3.js.map +1 -0
  183. package/dist/slack-GUQMDGLP.js +8 -0
  184. package/dist/slack-GUQMDGLP.js.map +1 -0
  185. package/dist/ssh-LY6WYVVD.js +9 -0
  186. package/dist/ssh-LY6WYVVD.js.map +1 -0
  187. package/dist/supabase-H3ZP4Y3W.js +10 -0
  188. package/dist/supabase-H3ZP4Y3W.js.map +1 -0
  189. package/dist/trello-WR6TEDYD.js +8 -0
  190. package/dist/trello-WR6TEDYD.js.map +1 -0
  191. 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