@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,2794 @@
1
+ import {
2
+ redactSshConfig
3
+ } from "./chunk-FZ2GJ4BJ.js";
4
+ import {
5
+ SCOPE_ERROR
6
+ } from "./chunk-U6QSXTUS.js";
7
+
8
+ // src/resource/ssh/ssh.ts
9
+ import {
10
+ PathSpec as PathSpec9,
11
+ RAMIndexCacheStore,
12
+ ResourceName as ResourceName33
13
+ } from "@struktoai/mirage-core";
14
+
15
+ // src/accessor/ssh.ts
16
+ import { Accessor } from "@struktoai/mirage-core";
17
+ import { homedir } from "os";
18
+ import { readFileSync } from "fs";
19
+ import { resolve } from "path";
20
+ function expandHome(p) {
21
+ if (p === "~") return homedir();
22
+ if (p.startsWith("~/")) return resolve(homedir(), p.slice(2));
23
+ return p;
24
+ }
25
+ var SSHAccessor = class extends Accessor {
26
+ constructor(config) {
27
+ super();
28
+ this.config = config;
29
+ }
30
+ config;
31
+ client = null;
32
+ sftpClient = null;
33
+ connectPromise = null;
34
+ async sftp() {
35
+ if (this.sftpClient !== null) return this.sftpClient;
36
+ if (this.connectPromise !== null) return this.connectPromise;
37
+ this.connectPromise = this.connect();
38
+ try {
39
+ return await this.connectPromise;
40
+ } finally {
41
+ this.connectPromise = null;
42
+ }
43
+ }
44
+ async connect() {
45
+ let ssh2Mod;
46
+ try {
47
+ ssh2Mod = await import("ssh2");
48
+ } catch (err) {
49
+ const msg = err instanceof Error ? err.message : String(err);
50
+ throw new Error(`ssh2 is required for the SSH resource \u2014 install it as a peer dep: ${msg}`);
51
+ }
52
+ const { Client: ClientCtor } = ssh2Mod;
53
+ const c = new ClientCtor();
54
+ const opts = {
55
+ host: this.config.hostname ?? this.config.host,
56
+ port: this.config.port ?? 22,
57
+ readyTimeout: (this.config.timeout ?? 30) * 1e3
58
+ };
59
+ if (this.config.username !== void 0) opts.username = this.config.username;
60
+ if (this.config.password !== void 0) opts.password = this.config.password;
61
+ if (this.config.identityFile !== void 0) {
62
+ opts.privateKey = readFileSync(expandHome(this.config.identityFile));
63
+ if (this.config.passphrase !== void 0) opts.passphrase = this.config.passphrase;
64
+ }
65
+ return new Promise((resolveFn, rejectFn) => {
66
+ c.on("ready", () => {
67
+ c.sftp((err, sftp) => {
68
+ if (err !== void 0) {
69
+ rejectFn(err);
70
+ return;
71
+ }
72
+ this.client = c;
73
+ this.sftpClient = sftp;
74
+ resolveFn(sftp);
75
+ });
76
+ });
77
+ c.on("error", rejectFn);
78
+ c.connect(opts);
79
+ });
80
+ }
81
+ close() {
82
+ if (this.client !== null) {
83
+ this.client.end();
84
+ this.client = null;
85
+ this.sftpClient = null;
86
+ }
87
+ return Promise.resolve();
88
+ }
89
+ };
90
+
91
+ // src/commands/builtin/ssh/basename.ts
92
+ import {
93
+ IOResult,
94
+ ResourceName,
95
+ command,
96
+ specOf
97
+ } from "@struktoai/mirage-core";
98
+ function basenameCommand(_accessor, paths, texts, _opts) {
99
+ const arg = texts[0] ?? paths[0]?.original ?? "";
100
+ const stripped = arg.replace(/\/+$/, "");
101
+ const idx = stripped.lastIndexOf("/");
102
+ const base = idx >= 0 ? stripped.slice(idx + 1) : stripped;
103
+ const out = new TextEncoder().encode(`${base}
104
+ `);
105
+ return [out, new IOResult()];
106
+ }
107
+ var SSH_BASENAME = command({
108
+ name: "basename",
109
+ resource: ResourceName.SSH,
110
+ spec: specOf("basename"),
111
+ fn: basenameCommand
112
+ });
113
+
114
+ // src/commands/builtin/ssh/cat/cat.ts
115
+ import {
116
+ AsyncLineIterator,
117
+ CachableAsyncIterator,
118
+ IOResult as IOResult2,
119
+ ProvisionResult,
120
+ ResourceName as ResourceName2,
121
+ command as command2,
122
+ concatAggregate,
123
+ resolveSource,
124
+ specOf as specOf2
125
+ } from "@struktoai/mirage-core";
126
+
127
+ // src/core/ssh/utils.ts
128
+ var S_IFMT = 61440;
129
+ var S_IFDIR = 16384;
130
+ var S_IFREG = 32768;
131
+ function stripPrefix(p) {
132
+ if (p.prefix && p.original.startsWith(p.prefix)) {
133
+ return p.original.slice(p.prefix.length) || "/";
134
+ }
135
+ return p.original;
136
+ }
137
+ function joinRoot(root, rel) {
138
+ const r = root.replace(/\/+$/, "");
139
+ const stripped = rel.replace(/^\/+/, "");
140
+ if (stripped === "") return r === "" ? "/" : r;
141
+ if (r === "") return `/${stripped}`;
142
+ return `${r}/${stripped}`;
143
+ }
144
+ function enoent(path) {
145
+ const err = new Error(`file not found: ${path}`);
146
+ err.code = "ENOENT";
147
+ return err;
148
+ }
149
+ function isNoSuchFile(err) {
150
+ if (err === null || err === void 0) return false;
151
+ if (typeof err !== "object") return false;
152
+ const code = err.code;
153
+ return code === 2;
154
+ }
155
+ function isDirectoryAttrs(attrs) {
156
+ if (attrs.mode === void 0) return false;
157
+ return (attrs.mode & S_IFMT) === S_IFDIR;
158
+ }
159
+ function isFileAttrs(attrs) {
160
+ if (attrs.mode === void 0) return false;
161
+ return (attrs.mode & S_IFMT) === S_IFREG;
162
+ }
163
+
164
+ // src/core/ssh/stream.ts
165
+ async function* stream(accessor, p) {
166
+ const sftp = await accessor.sftp();
167
+ const virtual = stripPrefix(p);
168
+ const remote = joinRoot(accessor.config.root ?? "/", virtual);
169
+ const rs = sftp.createReadStream(remote);
170
+ try {
171
+ for await (const chunk of rs) {
172
+ const buf = chunk;
173
+ yield new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
174
+ }
175
+ } catch (err) {
176
+ if (isNoSuchFile(err)) throw enoent(virtual);
177
+ throw err;
178
+ }
179
+ }
180
+ async function rangeRead(accessor, p, start, end) {
181
+ const sftp = await accessor.sftp();
182
+ const virtual = stripPrefix(p);
183
+ const remote = joinRoot(accessor.config.root ?? "/", virtual);
184
+ const rs = sftp.createReadStream(remote, { start, end: end - 1 });
185
+ const chunks = [];
186
+ let total = 0;
187
+ try {
188
+ for await (const chunk of rs) {
189
+ const buf = chunk;
190
+ const u8 = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
191
+ chunks.push(u8);
192
+ total += u8.byteLength;
193
+ }
194
+ } catch (err) {
195
+ if (isNoSuchFile(err)) throw enoent(virtual);
196
+ throw err;
197
+ }
198
+ const out = new Uint8Array(total);
199
+ let offset = 0;
200
+ for (const c of chunks) {
201
+ out.set(c, offset);
202
+ offset += c.byteLength;
203
+ }
204
+ return out;
205
+ }
206
+
207
+ // src/core/ssh/entry.ts
208
+ import { FileStat, FileType, guessType } from "@struktoai/mirage-core";
209
+ function attrsToFileStat(name, attrs) {
210
+ const modified = attrs.mtime !== void 0 ? new Date(attrs.mtime * 1e3).toISOString() : null;
211
+ const extra = {};
212
+ if (attrs.mode !== void 0) extra.mode = attrs.mode;
213
+ if (attrs.uid !== void 0) extra.uid = attrs.uid;
214
+ if (attrs.gid !== void 0) extra.gid = attrs.gid;
215
+ if (isDirectoryAttrs(attrs)) {
216
+ return new FileStat({
217
+ name,
218
+ size: null,
219
+ modified,
220
+ type: FileType.DIRECTORY,
221
+ extra
222
+ });
223
+ }
224
+ return new FileStat({
225
+ name,
226
+ size: attrs.size ?? null,
227
+ modified,
228
+ fingerprint: null,
229
+ type: guessType(name),
230
+ extra
231
+ });
232
+ }
233
+
234
+ // src/core/ssh/stat.ts
235
+ async function stat(accessor, p) {
236
+ const sftp = await accessor.sftp();
237
+ const virtual = stripPrefix(p);
238
+ const remote = joinRoot(accessor.config.root ?? "/", virtual);
239
+ const attrs = await new Promise((resolveFn, rejectFn) => {
240
+ sftp.lstat(remote, (err, stats) => {
241
+ if (err !== void 0) {
242
+ if (isNoSuchFile(err)) rejectFn(enoent(virtual));
243
+ else rejectFn(err);
244
+ return;
245
+ }
246
+ resolveFn(stats);
247
+ });
248
+ });
249
+ const cleaned = virtual.replace(/\/+$/, "");
250
+ const name = cleaned.length === 0 ? "/" : cleaned.split("/").pop() ?? "/";
251
+ return attrsToFileStat(name, attrs);
252
+ }
253
+
254
+ // src/commands/builtin/ssh/cat/cat.ts
255
+ var ENC = new TextEncoder();
256
+ async function* numberLinesStream(source) {
257
+ let num = 1;
258
+ const lineIter = new AsyncLineIterator(source);
259
+ for await (const line of lineIter) {
260
+ yield ENC.encode(` ${String(num)} `);
261
+ yield line;
262
+ yield ENC.encode("\n");
263
+ num += 1;
264
+ }
265
+ }
266
+ async function catProvision(accessor, paths, _texts, _opts) {
267
+ const [first] = paths;
268
+ if (first === void 0) return new ProvisionResult({ command: "cat" });
269
+ try {
270
+ const s = await stat(accessor, first);
271
+ return new ProvisionResult({
272
+ command: `cat ${first.original}`,
273
+ networkReadLow: s.size ?? 0,
274
+ networkReadHigh: s.size ?? 0,
275
+ readOps: 1
276
+ });
277
+ } catch {
278
+ return new ProvisionResult({ command: "cat" });
279
+ }
280
+ }
281
+ async function* chainStreams(streams) {
282
+ for (const s of streams) {
283
+ for await (const chunk of s) yield chunk;
284
+ }
285
+ }
286
+ async function catCommand(accessor, paths, texts, opts) {
287
+ const nFlag = opts.flags.n === true;
288
+ if (paths.length > 0) {
289
+ for (const p of paths) await stat(accessor, p);
290
+ const reads = {};
291
+ const cacheKeys = [];
292
+ const outputs = [];
293
+ for (const p of paths) {
294
+ const cachable = new CachableAsyncIterator(stream(accessor, p));
295
+ reads[p.stripPrefix] = cachable;
296
+ cacheKeys.push(p.stripPrefix);
297
+ outputs.push(cachable);
298
+ }
299
+ const merged = chainStreams(outputs);
300
+ const out = nFlag ? numberLinesStream(merged) : merged;
301
+ return [out, new IOResult2({ reads, cache: cacheKeys })];
302
+ }
303
+ try {
304
+ const source = resolveSource(opts.stdin, "cat: missing operand");
305
+ const out = nFlag ? numberLinesStream(source) : source;
306
+ return [out, new IOResult2()];
307
+ } catch (err) {
308
+ const msg = err instanceof Error ? err.message : String(err);
309
+ return [null, new IOResult2({ exitCode: 1, stderr: ENC.encode(`${msg}
310
+ `) })];
311
+ }
312
+ }
313
+ var SSH_CAT = command2({
314
+ name: "cat",
315
+ resource: ResourceName2.SSH,
316
+ spec: specOf2("cat"),
317
+ fn: catCommand,
318
+ provision: catProvision,
319
+ aggregate: concatAggregate
320
+ });
321
+
322
+ // src/commands/builtin/ssh/cp.ts
323
+ import {
324
+ IOResult as IOResult3,
325
+ PathSpec,
326
+ ResourceName as ResourceName3,
327
+ command as command3,
328
+ specOf as specOf3
329
+ } from "@struktoai/mirage-core";
330
+
331
+ // src/core/ssh/read.ts
332
+ async function read(accessor, p) {
333
+ const sftp = await accessor.sftp();
334
+ const virtual = stripPrefix(p);
335
+ const remote = joinRoot(accessor.config.root ?? "/", virtual);
336
+ return new Promise((resolveFn, rejectFn) => {
337
+ sftp.readFile(remote, (err, buf) => {
338
+ if (err !== void 0) {
339
+ if (isNoSuchFile(err)) rejectFn(enoent(virtual));
340
+ else rejectFn(err);
341
+ return;
342
+ }
343
+ resolveFn(new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength));
344
+ });
345
+ });
346
+ }
347
+
348
+ // src/core/ssh/write.ts
349
+ async function writeBytes(accessor, p, data) {
350
+ const sftp = await accessor.sftp();
351
+ const virtual = stripPrefix(p);
352
+ const remote = joinRoot(accessor.config.root ?? "/", virtual);
353
+ await new Promise((resolveFn, rejectFn) => {
354
+ sftp.writeFile(remote, Buffer.from(data), (err) => {
355
+ if (err) rejectFn(err);
356
+ else resolveFn();
357
+ });
358
+ });
359
+ }
360
+
361
+ // src/core/ssh/copy.ts
362
+ async function copy(accessor, src, dst) {
363
+ const data = await read(accessor, src);
364
+ await writeBytes(accessor, dst, data);
365
+ }
366
+
367
+ // src/core/ssh/find.ts
368
+ function fnmatch(name, pattern) {
369
+ let re = "^";
370
+ for (const ch of pattern) {
371
+ if (ch === "*") re += ".*";
372
+ else if (ch === "?") re += ".";
373
+ else if (/[.+^${}()|[\]\\]/.test(ch)) re += "\\" + ch;
374
+ else re += ch;
375
+ }
376
+ re += "$";
377
+ return new RegExp(re).test(name);
378
+ }
379
+ function norm(p) {
380
+ return `/${p.replace(/^\/+|\/+$/g, "")}`;
381
+ }
382
+ function isFileType(t) {
383
+ return t === "f" || t === "file";
384
+ }
385
+ function isDirType(t) {
386
+ return t === "d" || t === "directory";
387
+ }
388
+ function matches(entry, entryPath, isDir, depth, opts) {
389
+ if (opts.minDepth !== null && opts.minDepth !== void 0 && depth < opts.minDepth) return false;
390
+ if (isFileType(opts.type) && isDir) return false;
391
+ if (isDirType(opts.type) && !isDir) return false;
392
+ const basename = entryPath.slice(entryPath.lastIndexOf("/") + 1);
393
+ if (opts.orNames !== null && opts.orNames !== void 0 && opts.orNames.length > 0) {
394
+ if (!opts.orNames.some((pat) => fnmatch(basename, pat))) return false;
395
+ } else if (opts.name !== null && opts.name !== void 0) {
396
+ if (!fnmatch(basename, opts.name)) return false;
397
+ }
398
+ if (opts.iname !== null && opts.iname !== void 0 && !fnmatch(basename.toLowerCase(), opts.iname.toLowerCase())) {
399
+ return false;
400
+ }
401
+ if (opts.nameExclude !== null && opts.nameExclude !== void 0 && fnmatch(basename, opts.nameExclude)) {
402
+ return false;
403
+ }
404
+ if (opts.pathPattern !== null && opts.pathPattern !== void 0 && !fnmatch(entryPath, opts.pathPattern)) {
405
+ return false;
406
+ }
407
+ const size = entry.attrs.size;
408
+ if (opts.minSize !== null && opts.minSize !== void 0 && size < opts.minSize) return false;
409
+ if (opts.maxSize !== null && opts.maxSize !== void 0 && size > opts.maxSize) return false;
410
+ if (opts.mtimeMin !== null && opts.mtimeMin !== void 0 || opts.mtimeMax !== null && opts.mtimeMax !== void 0) {
411
+ const mtime = entry.attrs.mtime;
412
+ if (opts.mtimeMin !== null && opts.mtimeMin !== void 0 && mtime < opts.mtimeMin) return false;
413
+ if (opts.mtimeMax !== null && opts.mtimeMax !== void 0 && mtime > opts.mtimeMax) return false;
414
+ }
415
+ return true;
416
+ }
417
+ async function readRemoteDir(accessor, remote) {
418
+ const sftp = await accessor.sftp();
419
+ return new Promise((resolveFn, rejectFn) => {
420
+ sftp.readdir(remote, (err, entries) => {
421
+ if (err !== void 0) {
422
+ const code = err.code;
423
+ if (code === 2) {
424
+ resolveFn(null);
425
+ return;
426
+ }
427
+ rejectFn(err);
428
+ return;
429
+ }
430
+ resolveFn(entries);
431
+ });
432
+ });
433
+ }
434
+ async function walk(ctx, virtual, depth) {
435
+ const opts = ctx.options;
436
+ if (opts.maxDepth !== null && opts.maxDepth !== void 0 && depth > opts.maxDepth) return;
437
+ const remote = joinRoot(ctx.accessor.config.root ?? "/", virtual);
438
+ const entries = await readRemoteDir(ctx.accessor, remote);
439
+ if (entries === null) return;
440
+ for (const entry of entries) {
441
+ if (entry.filename === "." || entry.filename === "..") continue;
442
+ const childPath = virtual === "/" ? `/${entry.filename}` : `${virtual}/${entry.filename}`;
443
+ const isDir = isDirectoryAttrs(entry.attrs);
444
+ if (isFileType(opts.type) && isDir) {
445
+ await walk(ctx, childPath, depth + 1);
446
+ continue;
447
+ }
448
+ if (matches(entry, childPath, isDir, depth + 1, opts)) {
449
+ ctx.results.push(childPath);
450
+ }
451
+ if (isDir) {
452
+ await walk(ctx, childPath, depth + 1);
453
+ }
454
+ }
455
+ }
456
+ async function find(accessor, p, options = {}) {
457
+ const virtual = norm(stripPrefix(p));
458
+ const results = [];
459
+ const baseDepth = (virtual.match(/\//g) ?? []).length;
460
+ await walk({ accessor, options, results, baseDepth }, virtual, 0);
461
+ results.sort();
462
+ return results;
463
+ }
464
+
465
+ // src/commands/builtin/ssh/cp.ts
466
+ async function exists(accessor, path) {
467
+ try {
468
+ await stat(accessor, path);
469
+ return true;
470
+ } catch {
471
+ return false;
472
+ }
473
+ }
474
+ function toPathSpec(strPath, prefix) {
475
+ return new PathSpec({
476
+ original: strPath,
477
+ directory: strPath,
478
+ resolved: false,
479
+ prefix
480
+ });
481
+ }
482
+ async function cpCommand(accessor, paths, _texts, opts) {
483
+ if (paths.length < 2) {
484
+ return [
485
+ null,
486
+ new IOResult3({
487
+ exitCode: 1,
488
+ stderr: new TextEncoder().encode("cp: missing operand\n")
489
+ })
490
+ ];
491
+ }
492
+ const recursive = opts.flags.r === true || opts.flags.R === true || opts.flags.a === true;
493
+ const noClobber = opts.flags.n === true;
494
+ const verbose = opts.flags.v === true;
495
+ const dst = paths[paths.length - 1];
496
+ if (dst === void 0) return [null, new IOResult3()];
497
+ const lines = [];
498
+ if (recursive) {
499
+ const src = paths[0];
500
+ if (src === void 0) return [null, new IOResult3()];
501
+ const srcBase = src.stripPrefix.replace(/\/+$/, "");
502
+ const dstBase = dst.stripPrefix.replace(/\/+$/, "");
503
+ const entries = await find(accessor, src, { type: "f" });
504
+ for (const entry of entries) {
505
+ const rel = entry.slice(srcBase.length);
506
+ const dstPath = dstBase + rel;
507
+ const dstSpec = toPathSpec(dstPath, dst.prefix);
508
+ if (noClobber && await exists(accessor, dstSpec)) continue;
509
+ const srcSpec = toPathSpec(entry, src.prefix);
510
+ await copy(accessor, srcSpec, dstSpec);
511
+ if (verbose) lines.push(`${entry} -> ${dstPath}`);
512
+ }
513
+ const out2 = lines.length > 0 ? new TextEncoder().encode(lines.join("\n") + "\n") : null;
514
+ return [out2, new IOResult3()];
515
+ }
516
+ const sources = paths.slice(0, -1);
517
+ for (const src of sources) {
518
+ if (noClobber && await exists(accessor, dst)) continue;
519
+ await copy(accessor, src, dst);
520
+ if (verbose) lines.push(`${src.original} -> ${dst.original}`);
521
+ }
522
+ const out = lines.length > 0 ? new TextEncoder().encode(lines.join("\n") + "\n") : null;
523
+ return [out, new IOResult3()];
524
+ }
525
+ var SSH_CP = command3({
526
+ name: "cp",
527
+ resource: ResourceName3.SSH,
528
+ spec: specOf3("cp"),
529
+ fn: cpCommand,
530
+ write: true
531
+ });
532
+
533
+ // src/commands/builtin/ssh/dirname.ts
534
+ import {
535
+ IOResult as IOResult4,
536
+ ResourceName as ResourceName4,
537
+ command as command4,
538
+ specOf as specOf4
539
+ } from "@struktoai/mirage-core";
540
+ function dirnameCommand(_accessor, paths, texts, _opts) {
541
+ const arg = texts[0] ?? paths[0]?.original ?? "";
542
+ const stripped = arg.replace(/\/+$/, "");
543
+ const idx = stripped.lastIndexOf("/");
544
+ const dir = idx > 0 ? stripped.slice(0, idx) : idx === 0 ? "/" : ".";
545
+ const out = new TextEncoder().encode(`${dir}
546
+ `);
547
+ return [out, new IOResult4()];
548
+ }
549
+ var SSH_DIRNAME = command4({
550
+ name: "dirname",
551
+ resource: ResourceName4.SSH,
552
+ spec: specOf4("dirname"),
553
+ fn: dirnameCommand
554
+ });
555
+
556
+ // src/commands/builtin/ssh/du.ts
557
+ import {
558
+ IOResult as IOResult5,
559
+ PathSpec as PathSpec2,
560
+ ResourceName as ResourceName5,
561
+ command as command5,
562
+ specOf as specOf5
563
+ } from "@struktoai/mirage-core";
564
+
565
+ // src/core/ssh/du.ts
566
+ function norm2(p) {
567
+ return `/${p.replace(/^\/+|\/+$/g, "")}`;
568
+ }
569
+ async function readRemoteDir2(accessor, remote) {
570
+ const sftp = await accessor.sftp();
571
+ return new Promise((resolveFn, rejectFn) => {
572
+ sftp.readdir(remote, (err, entries) => {
573
+ if (err !== void 0) {
574
+ const code = err.code;
575
+ if (code === 2) {
576
+ resolveFn(null);
577
+ return;
578
+ }
579
+ rejectFn(err);
580
+ return;
581
+ }
582
+ resolveFn(entries);
583
+ });
584
+ });
585
+ }
586
+ async function statRemote(accessor, remote) {
587
+ const sftp = await accessor.sftp();
588
+ return new Promise((resolveFn, rejectFn) => {
589
+ sftp.lstat(remote, (err, stats) => {
590
+ if (err !== void 0) {
591
+ const code = err.code;
592
+ if (code === 2) {
593
+ resolveFn(null);
594
+ return;
595
+ }
596
+ rejectFn(err);
597
+ return;
598
+ }
599
+ resolveFn({ mode: stats.mode, size: stats.size });
600
+ });
601
+ });
602
+ }
603
+ async function walkSizes(accessor, virtual) {
604
+ const remote = joinRoot(accessor.config.root ?? "/", virtual);
605
+ const st = await statRemote(accessor, remote);
606
+ if (st === null) return 0;
607
+ if (isFileAttrs(st)) return st.size;
608
+ if (!isDirectoryAttrs(st)) return 0;
609
+ const entries = await readRemoteDir2(accessor, remote);
610
+ if (entries === null) return 0;
611
+ let total = 0;
612
+ for (const entry of entries) {
613
+ if (entry.filename === "." || entry.filename === "..") continue;
614
+ const child = virtual === "/" ? `/${entry.filename}` : `${virtual}/${entry.filename}`;
615
+ if (isDirectoryAttrs(entry.attrs)) {
616
+ total += await walkSizes(accessor, child);
617
+ } else {
618
+ total += entry.attrs.size;
619
+ }
620
+ }
621
+ return total;
622
+ }
623
+ async function walkAll(accessor, virtual, entries) {
624
+ const remote = joinRoot(accessor.config.root ?? "/", virtual);
625
+ const st = await statRemote(accessor, remote);
626
+ if (st === null) return 0;
627
+ if (isFileAttrs(st)) {
628
+ entries.push([virtual, st.size]);
629
+ return st.size;
630
+ }
631
+ if (!isDirectoryAttrs(st)) return 0;
632
+ const list = await readRemoteDir2(accessor, remote);
633
+ if (list === null) return 0;
634
+ let total = 0;
635
+ for (const entry of list) {
636
+ if (entry.filename === "." || entry.filename === "..") continue;
637
+ const child = virtual === "/" ? `/${entry.filename}` : `${virtual}/${entry.filename}`;
638
+ if (isDirectoryAttrs(entry.attrs)) {
639
+ total += await walkAll(accessor, child, entries);
640
+ } else {
641
+ const size = entry.attrs.size;
642
+ entries.push([child, size]);
643
+ total += size;
644
+ }
645
+ }
646
+ return total;
647
+ }
648
+ async function du(accessor, p) {
649
+ const virtual = stripPrefix(p);
650
+ return walkSizes(accessor, virtual);
651
+ }
652
+ async function duAll(accessor, p) {
653
+ const virtual = norm2(stripPrefix(p));
654
+ const entries = [];
655
+ const total = await walkAll(accessor, virtual, entries);
656
+ entries.sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
657
+ return [entries, total];
658
+ }
659
+
660
+ // src/commands/builtin/ssh/du.ts
661
+ function humanSize(n) {
662
+ const units = ["", "K", "M", "G", "T"];
663
+ let v = n;
664
+ let i = 0;
665
+ while (v >= 1024 && i < units.length - 1) {
666
+ v /= 1024;
667
+ i += 1;
668
+ }
669
+ const s = v >= 10 || i === 0 ? Math.round(v).toString() : v.toFixed(1);
670
+ return `${s}${units[i] ?? ""}`;
671
+ }
672
+ function depthOf(entryPath, basePath) {
673
+ const base = basePath.replace(/\/+$/, "");
674
+ const rel = entryPath.replace(/\/+$/, "").slice(base.length);
675
+ if (!rel) return 0;
676
+ return (rel.replace(/^\/+|\/+$/g, "").match(/\//g) ?? []).length + 1;
677
+ }
678
+ async function duCommand(accessor, paths, _texts, opts) {
679
+ const human = opts.flags.h === true;
680
+ const summarize = opts.flags.s === true;
681
+ const all = opts.flags.a === true;
682
+ const cumulative = opts.flags.c === true;
683
+ const maxDepthRaw = opts.flags["max-depth"];
684
+ const maxDepth = typeof maxDepthRaw === "string" ? Number.parseInt(maxDepthRaw, 10) : null;
685
+ const fmt = (size) => human ? humanSize(size) : String(size);
686
+ const targets = paths.length > 0 ? paths : [new PathSpec2({ original: "/", directory: "/", resolved: false })];
687
+ const path = targets[0];
688
+ if (path === void 0) return [null, new IOResult5()];
689
+ if (summarize) {
690
+ const total = await du(accessor, path);
691
+ let output = `${fmt(total)} ${path.original}`;
692
+ if (cumulative) output += `
693
+ ${fmt(total)} total`;
694
+ return [new TextEncoder().encode(output), new IOResult5()];
695
+ }
696
+ const [allEntriesRaw, totalFromAll] = await duAll(accessor, path);
697
+ let entries = allEntriesRaw;
698
+ if (entries.length === 0) {
699
+ const total = await du(accessor, path);
700
+ let output = `${fmt(total)} ${path.original}`;
701
+ if (cumulative) output += `
702
+ ${fmt(total)} total`;
703
+ return [new TextEncoder().encode(output), new IOResult5()];
704
+ }
705
+ if (!all) {
706
+ entries = entries.filter(([p]) => p === path.original);
707
+ }
708
+ if (maxDepth !== null) {
709
+ entries = entries.filter(([p]) => depthOf(p, path.original) <= maxDepth);
710
+ }
711
+ if (entries.length === 0) {
712
+ const total = totalFromAll !== 0 ? totalFromAll : await du(accessor, path);
713
+ let output = `${fmt(total)} ${path.original}`;
714
+ if (cumulative) output += `
715
+ ${fmt(total)} total`;
716
+ return [new TextEncoder().encode(output), new IOResult5()];
717
+ }
718
+ const lines = entries.map(([p, sz]) => `${fmt(sz)} ${p}`);
719
+ if (cumulative) {
720
+ const grand = entries.reduce((acc, [, sz]) => acc + sz, 0);
721
+ lines.push(`${fmt(grand)} total`);
722
+ }
723
+ return [new TextEncoder().encode(lines.join("\n")), new IOResult5()];
724
+ }
725
+ var SSH_DU = command5({
726
+ name: "du",
727
+ resource: ResourceName5.SSH,
728
+ spec: specOf5("du"),
729
+ fn: duCommand
730
+ });
731
+
732
+ // src/commands/builtin/ssh/file/file.ts
733
+ import {
734
+ FileType as FileType2,
735
+ IOResult as IOResult6,
736
+ ResourceName as ResourceName6,
737
+ command as command6,
738
+ detectFileType,
739
+ formatFileResult,
740
+ specOf as specOf6
741
+ } from "@struktoai/mirage-core";
742
+ var ENC2 = new TextEncoder();
743
+ async function fileCommand(accessor, paths, texts, opts) {
744
+ if (paths.length === 0) {
745
+ return [null, new IOResult6({ exitCode: 1, stderr: ENC2.encode("file: missing operand\n") })];
746
+ }
747
+ const brief = opts.flags.b === true;
748
+ const mime = opts.flags.i === true;
749
+ const lines = [];
750
+ for (const p of paths) {
751
+ const s = await stat(accessor, p);
752
+ if (s.type === FileType2.DIRECTORY) {
753
+ lines.push(formatFileResult(p.original, FileType2.DIRECTORY, brief, mime));
754
+ continue;
755
+ }
756
+ let header;
757
+ try {
758
+ const raw = await read(accessor, p);
759
+ header = raw.subarray(0, 512);
760
+ } catch {
761
+ header = new Uint8Array(0);
762
+ }
763
+ const result = detectFileType(header, s);
764
+ lines.push(formatFileResult(p.original, result, brief, mime));
765
+ }
766
+ const out = ENC2.encode(lines.join("\n"));
767
+ return [out, new IOResult6()];
768
+ }
769
+ var SSH_FILE = command6({
770
+ name: "file",
771
+ resource: ResourceName6.SSH,
772
+ spec: specOf6("file"),
773
+ fn: fileCommand
774
+ });
775
+
776
+ // src/commands/builtin/ssh/find.ts
777
+ import {
778
+ IOResult as IOResult7,
779
+ PathSpec as PathSpec3,
780
+ ResourceName as ResourceName7,
781
+ command as command7,
782
+ specOf as specOf7
783
+ } from "@struktoai/mirage-core";
784
+ function parseSize(spec) {
785
+ const suffixes = { c: 1, k: 1024, M: 1024 ** 2, G: 1024 ** 3 };
786
+ const sign = spec.startsWith("+") ? "+" : spec.startsWith("-") ? "-" : "";
787
+ const raw = sign === "" ? spec : spec.slice(1);
788
+ const lastChar = raw.slice(-1);
789
+ const mult = suffixes[lastChar] ?? 1;
790
+ const numPart = lastChar in suffixes ? raw.slice(0, -1) : raw;
791
+ const num = Number.parseInt(numPart, 10) * mult;
792
+ if (sign === "+") return [num, null];
793
+ if (sign === "-") return [null, num];
794
+ return [num, num];
795
+ }
796
+ function parseMtime(spec) {
797
+ const now = Date.now() / 1e3;
798
+ const day = 86400;
799
+ const n = Number.parseInt(spec.replace(/^[+-]/, ""), 10);
800
+ if (spec.startsWith("+")) return [null, now - n * day];
801
+ if (spec.startsWith("-")) return [now - n * day, null];
802
+ return [now - (n + 1) * day, now - n * day];
803
+ }
804
+ function extractNotName(texts) {
805
+ for (let i = 0; i < texts.length; i++) {
806
+ const pat = texts[i + 2];
807
+ if (texts[i] === "-not" && texts[i + 1] === "-name" && pat !== void 0) {
808
+ return pat;
809
+ }
810
+ }
811
+ return null;
812
+ }
813
+ function extractOrNames(name, texts) {
814
+ const names = [];
815
+ if (name !== null) names.push(name);
816
+ let i = 0;
817
+ while (i < texts.length) {
818
+ const pat = texts[i + 2];
819
+ if (texts[i] === "-or" && texts[i + 1] === "-name" && pat !== void 0) {
820
+ names.push(pat);
821
+ i += 3;
822
+ } else {
823
+ i += 1;
824
+ }
825
+ }
826
+ return names;
827
+ }
828
+ async function findCommand(accessor, paths, texts, opts) {
829
+ const nameFlag = typeof opts.flags.name === "string" ? opts.flags.name : null;
830
+ const inameFlag = typeof opts.flags.iname === "string" ? opts.flags.iname : null;
831
+ const typeFlag = typeof opts.flags.type === "string" ? opts.flags.type : null;
832
+ const pathFlag = typeof opts.flags.path === "string" ? opts.flags.path : null;
833
+ const maxDepthFlag = typeof opts.flags.maxdepth === "string" ? opts.flags.maxdepth : null;
834
+ const minDepthFlag = typeof opts.flags.mindepth === "string" ? opts.flags.mindepth : null;
835
+ const sizeFlag = typeof opts.flags.size === "string" ? opts.flags.size : null;
836
+ const mtimeFlag = typeof opts.flags.mtime === "string" ? opts.flags.mtime : null;
837
+ const targets = paths.length > 0 ? paths : [new PathSpec3({ original: "/", directory: "/", resolved: false })];
838
+ const findType = typeFlag === "f" ? "f" : typeFlag === "d" ? "d" : null;
839
+ const maxDepth = maxDepthFlag !== null ? Number.parseInt(maxDepthFlag, 10) : null;
840
+ const minDepth = minDepthFlag !== null ? Number.parseInt(minDepthFlag, 10) : null;
841
+ const [minSize, maxSize] = sizeFlag !== null ? parseSize(sizeFlag) : [null, null];
842
+ const [mtimeMin, mtimeMax] = mtimeFlag !== null ? parseMtime(mtimeFlag) : [null, null];
843
+ const nameExclude = extractNotName(texts);
844
+ const orNames = extractOrNames(nameFlag, texts);
845
+ const matches2 = [];
846
+ for (const root of targets) {
847
+ let keys;
848
+ try {
849
+ keys = await find(accessor, root, {
850
+ name: nameFlag,
851
+ iname: inameFlag,
852
+ type: findType,
853
+ ...maxDepth !== null ? { maxDepth } : {},
854
+ ...minDepth !== null ? { minDepth } : {},
855
+ ...minSize !== null ? { minSize } : {},
856
+ ...maxSize !== null ? { maxSize } : {},
857
+ ...mtimeMin !== null ? { mtimeMin } : {},
858
+ ...mtimeMax !== null ? { mtimeMax } : {},
859
+ ...nameExclude !== null ? { nameExclude } : {},
860
+ ...pathFlag !== null ? { pathPattern: pathFlag } : {},
861
+ ...orNames.length > 1 ? { orNames } : {}
862
+ });
863
+ } catch {
864
+ continue;
865
+ }
866
+ const rootKey = root.stripPrefix.replace(/\/+$/, "") || "/";
867
+ for (const key of keys) {
868
+ const displayPath = root.original === "/" ? key : root.original.replace(/\/+$/, "") + key.slice(rootKey === "/" ? 0 : rootKey.length);
869
+ matches2.push(displayPath);
870
+ }
871
+ }
872
+ matches2.sort();
873
+ const out = new TextEncoder().encode(matches2.join("\n"));
874
+ return [out, new IOResult7()];
875
+ }
876
+ var SSH_FIND = command7({
877
+ name: "find",
878
+ resource: ResourceName7.SSH,
879
+ spec: specOf7("find"),
880
+ fn: findCommand
881
+ });
882
+
883
+ // src/commands/builtin/ssh/grep/grep.ts
884
+ import {
885
+ FileType as FileType3,
886
+ IOResult as IOResult8,
887
+ PathSpec as PathSpec4,
888
+ ResourceName as ResourceName8,
889
+ command as command8,
890
+ compilePattern,
891
+ exitOnEmpty,
892
+ grepLines,
893
+ grepStream,
894
+ materialize,
895
+ prefixAggregate,
896
+ quietMatch,
897
+ resolveSource as resolveSource2,
898
+ specOf as specOf8
899
+ } from "@struktoai/mirage-core";
900
+ var ENC3 = new TextEncoder();
901
+ var DEC = new TextDecoder("utf-8", { fatal: false });
902
+ function getPattern(texts, flags) {
903
+ if (typeof flags.e === "string") return flags.e;
904
+ if (texts.length > 0 && texts[0] !== void 0) return texts[0];
905
+ throw new Error("grep: usage: grep [flags] pattern [path]");
906
+ }
907
+ function parseFlags(flags) {
908
+ const toInt = (v) => typeof v === "string" ? Number.parseInt(v, 10) : null;
909
+ const aCtx = toInt(flags.A);
910
+ const bCtx = toInt(flags.B);
911
+ const cCtx = toInt(flags.C);
912
+ return {
913
+ ignoreCase: flags.i === true,
914
+ invert: flags.v === true,
915
+ lineNumbers: flags.n === true,
916
+ countOnly: flags.c === true,
917
+ filesOnly: flags.args_l === true || flags.l === true,
918
+ wholeWord: flags.w === true,
919
+ fixedString: flags.F === true,
920
+ onlyMatching: flags.o === true,
921
+ maxCount: toInt(flags.m),
922
+ quiet: flags.q === true,
923
+ afterContext: aCtx ?? cCtx ?? 0,
924
+ beforeContext: bCtx ?? cCtx ?? 0
925
+ };
926
+ }
927
+ async function readFile(accessor, p) {
928
+ return materialize(stream(accessor, p));
929
+ }
930
+ function splitLinesNoTrailing(text) {
931
+ const stripped = text.endsWith("\n") ? text.slice(0, -1) : text;
932
+ return stripped === "" ? [] : stripped.split("\n");
933
+ }
934
+ async function expandRecursive(accessor, paths) {
935
+ const expanded = [];
936
+ for (const p of paths) {
937
+ try {
938
+ const st = await stat(accessor, p);
939
+ if (st.type === FileType3.DIRECTORY) {
940
+ const entries = await find(accessor, p, { type: "f" });
941
+ for (const entry of entries) {
942
+ expanded.push(
943
+ new PathSpec4({
944
+ original: entry,
945
+ directory: entry,
946
+ resolved: false,
947
+ prefix: p.prefix
948
+ })
949
+ );
950
+ }
951
+ } else {
952
+ expanded.push(p);
953
+ }
954
+ } catch {
955
+ expanded.push(p);
956
+ }
957
+ }
958
+ return expanded;
959
+ }
960
+ async function grepImpl(name, accessor, paths, texts, opts) {
961
+ let pattern;
962
+ try {
963
+ pattern = getPattern(texts, opts.flags);
964
+ } catch (err) {
965
+ const msg = err instanceof Error ? err.message : String(err);
966
+ return [null, new IOResult8({ exitCode: 2, stderr: ENC3.encode(`${msg}
967
+ `) })];
968
+ }
969
+ const f = parseFlags(opts.flags);
970
+ const recursive = opts.flags.r === true || opts.flags.R === true;
971
+ if (recursive && paths.length > 0) {
972
+ const pat2 = compilePattern(pattern, f.ignoreCase, f.fixedString, f.wholeWord);
973
+ const expanded = await expandRecursive(accessor, paths);
974
+ const allResults = [];
975
+ for (const p of expanded) {
976
+ const data = splitLinesNoTrailing(DEC.decode(await readFile(accessor, p)));
977
+ const hits = grepLines(p.original, data, pat2, f);
978
+ if (f.countOnly) {
979
+ if (hits.length > 0) allResults.push(`${p.original}:${hits[0] ?? ""}`);
980
+ } else if (f.filesOnly) {
981
+ for (const h of hits) allResults.push(h);
982
+ } else {
983
+ for (const h of hits) allResults.push(`${p.original}:${h}`);
984
+ }
985
+ }
986
+ if (allResults.length === 0) return [new Uint8Array(0), new IOResult8({ exitCode: 1 })];
987
+ const out = ENC3.encode(allResults.join("\n"));
988
+ return [out, new IOResult8()];
989
+ }
990
+ if (paths.length > 0) {
991
+ const pat2 = compilePattern(pattern, f.ignoreCase, f.fixedString, f.wholeWord);
992
+ if (paths.length > 1) {
993
+ const allResults = [];
994
+ for (const p of paths) {
995
+ const data = splitLinesNoTrailing(DEC.decode(await readFile(accessor, p)));
996
+ const hits = grepLines(p.original, data, pat2, f);
997
+ if (f.countOnly) {
998
+ if (hits.length > 0) allResults.push(`${p.original}:${hits[0] ?? ""}`);
999
+ } else if (f.filesOnly) {
1000
+ for (const h of hits) allResults.push(h);
1001
+ } else {
1002
+ for (const h of hits) allResults.push(`${p.original}:${h}`);
1003
+ }
1004
+ }
1005
+ if (allResults.length === 0) return [new Uint8Array(0), new IOResult8({ exitCode: 1 })];
1006
+ const out = ENC3.encode(allResults.join("\n"));
1007
+ return [out, new IOResult8()];
1008
+ }
1009
+ const first = paths[0];
1010
+ if (first === void 0) return [null, new IOResult8()];
1011
+ await stat(accessor, first);
1012
+ if (f.filesOnly) {
1013
+ const data = splitLinesNoTrailing(DEC.decode(await readFile(accessor, first)));
1014
+ const hits = grepLines(first.original, data, pat2, f);
1015
+ if (hits.length === 0) return [new Uint8Array(0), new IOResult8({ exitCode: 1 })];
1016
+ return [ENC3.encode(hits.join("\n")), new IOResult8()];
1017
+ }
1018
+ const source2 = stream(accessor, first);
1019
+ const stream3 = grepStream(source2, pat2, f);
1020
+ if (f.quiet) {
1021
+ const io3 = new IOResult8({ exitCode: 1 });
1022
+ return [quietMatch(stream3, io3), io3];
1023
+ }
1024
+ const io2 = new IOResult8();
1025
+ return [exitOnEmpty(stream3, io2), io2];
1026
+ }
1027
+ let source;
1028
+ try {
1029
+ source = resolveSource2(opts.stdin, `${name}: usage: ${name} [flags] pattern [path]`);
1030
+ } catch (err) {
1031
+ const msg = err instanceof Error ? err.message : String(err);
1032
+ return [null, new IOResult8({ exitCode: 2, stderr: ENC3.encode(`${msg}
1033
+ `) })];
1034
+ }
1035
+ const pat = compilePattern(pattern, f.ignoreCase, f.fixedString, f.wholeWord);
1036
+ const stream2 = grepStream(source, pat, f);
1037
+ if (f.quiet) {
1038
+ const io2 = new IOResult8({ exitCode: 1 });
1039
+ return [quietMatch(stream2, io2), io2];
1040
+ }
1041
+ const io = new IOResult8();
1042
+ return [exitOnEmpty(stream2, io), io];
1043
+ }
1044
+ var grepFn = (accessor, paths, texts, opts) => {
1045
+ return grepImpl("grep", accessor, paths, texts, opts);
1046
+ };
1047
+ var SSH_GREP = command8({
1048
+ name: "grep",
1049
+ resource: ResourceName8.SSH,
1050
+ spec: specOf8("grep"),
1051
+ fn: grepFn,
1052
+ aggregate: prefixAggregate
1053
+ });
1054
+
1055
+ // src/commands/builtin/ssh/head/head.ts
1056
+ import {
1057
+ AsyncLineIterator as AsyncLineIterator2,
1058
+ IOResult as IOResult9,
1059
+ Precision,
1060
+ ProvisionResult as ProvisionResult2,
1061
+ ResourceName as ResourceName9,
1062
+ command as command9,
1063
+ headerAggregate,
1064
+ resolveSource as resolveSource3,
1065
+ specOf as specOf9
1066
+ } from "@struktoai/mirage-core";
1067
+ var ENC4 = new TextEncoder();
1068
+ async function* headStream(source, lines, bytesMode) {
1069
+ if (bytesMode !== null) {
1070
+ let remaining = bytesMode;
1071
+ for await (const chunk of source) {
1072
+ if (chunk.byteLength <= remaining) {
1073
+ yield chunk;
1074
+ remaining -= chunk.byteLength;
1075
+ if (remaining <= 0) return;
1076
+ } else {
1077
+ yield chunk.slice(0, remaining);
1078
+ return;
1079
+ }
1080
+ }
1081
+ return;
1082
+ }
1083
+ let count = 0;
1084
+ const lineIter = new AsyncLineIterator2(source);
1085
+ for await (const line of lineIter) {
1086
+ const out = new Uint8Array(line.byteLength + 1);
1087
+ out.set(line, 0);
1088
+ out[line.byteLength] = 10;
1089
+ yield out;
1090
+ count += 1;
1091
+ if (count >= lines) return;
1092
+ }
1093
+ }
1094
+ async function* headMulti(accessor, paths, lines, bytesMode) {
1095
+ for (let i = 0; i < paths.length; i++) {
1096
+ const p = paths[i];
1097
+ if (p === void 0) continue;
1098
+ if (paths.length > 1) {
1099
+ const prefix = i > 0 ? "\n" : "";
1100
+ yield ENC4.encode(`${prefix}==> ${p.original} <==
1101
+ `);
1102
+ }
1103
+ const source = stream(accessor, p);
1104
+ for await (const chunk of headStream(source, lines, bytesMode)) yield chunk;
1105
+ }
1106
+ }
1107
+ async function headProvision(accessor, paths, texts, opts) {
1108
+ const [first] = paths;
1109
+ if (first === void 0) return new ProvisionResult2({ command: "head" });
1110
+ try {
1111
+ const s = await stat(accessor, first);
1112
+ const fileSize = s.size ?? 0;
1113
+ const nFlag = typeof opts.flags.n === "string" ? Number.parseInt(opts.flags.n, 10) : null;
1114
+ const lines = nFlag !== null && Number.isFinite(nFlag) ? nFlag : 10;
1115
+ const avgLine = 80;
1116
+ const low = Math.min(lines * avgLine, fileSize);
1117
+ return new ProvisionResult2({
1118
+ command: `head ${first.original}`,
1119
+ networkReadLow: low,
1120
+ networkReadHigh: fileSize,
1121
+ readOps: 1,
1122
+ precision: Precision.RANGE
1123
+ });
1124
+ } catch {
1125
+ return new ProvisionResult2({ command: "head" });
1126
+ }
1127
+ }
1128
+ async function headCommand(accessor, paths, texts, opts) {
1129
+ const nRaw = typeof opts.flags.n === "string" ? opts.flags.n : null;
1130
+ const cRaw = typeof opts.flags.c === "string" ? opts.flags.c : null;
1131
+ const lineCount = nRaw !== null ? Number.parseInt(nRaw, 10) : 10;
1132
+ const byteCount = cRaw !== null ? Number.parseInt(cRaw, 10) : null;
1133
+ if (paths.length > 0) {
1134
+ for (const p of paths) await stat(accessor, p);
1135
+ return [headMulti(accessor, paths, lineCount, byteCount), new IOResult9()];
1136
+ }
1137
+ try {
1138
+ const source = resolveSource3(opts.stdin, "head: missing operand");
1139
+ return [headStream(source, lineCount, byteCount), new IOResult9()];
1140
+ } catch (err) {
1141
+ const msg = err instanceof Error ? err.message : String(err);
1142
+ return [null, new IOResult9({ exitCode: 1, stderr: ENC4.encode(`${msg}
1143
+ `) })];
1144
+ }
1145
+ }
1146
+ var SSH_HEAD = command9({
1147
+ name: "head",
1148
+ resource: ResourceName9.SSH,
1149
+ spec: specOf9("head"),
1150
+ fn: headCommand,
1151
+ provision: headProvision,
1152
+ aggregate: headerAggregate
1153
+ });
1154
+
1155
+ // src/commands/builtin/ssh/jq.ts
1156
+ import {
1157
+ IOResult as IOResult10,
1158
+ Precision as Precision2,
1159
+ ProvisionResult as ProvisionResult3,
1160
+ ResourceName as ResourceName10,
1161
+ command as command10,
1162
+ evalJsonlStream,
1163
+ isJsonlPath,
1164
+ isStreamableJsonlExpr,
1165
+ jqEval,
1166
+ materialize as materialize2,
1167
+ parseJsonAuto,
1168
+ parseJsonPath,
1169
+ readStdinAsync,
1170
+ specOf as specOf10
1171
+ } from "@struktoai/mirage-core";
1172
+ var ENC5 = new TextEncoder();
1173
+ async function jqProvision(accessor, paths, texts, _opts) {
1174
+ const [first] = paths;
1175
+ const [expr] = texts;
1176
+ if (first === void 0 || expr === void 0) return new ProvisionResult3({ command: "jq" });
1177
+ try {
1178
+ const s = await stat(accessor, first);
1179
+ const fileSize = s.size ?? 0;
1180
+ if (isJsonlPath(first.original) && isStreamableJsonlExpr(expr)) {
1181
+ return new ProvisionResult3({
1182
+ command: `jq '${expr}' ${first.original}`,
1183
+ networkReadLow: 0,
1184
+ networkReadHigh: fileSize,
1185
+ readOps: 1,
1186
+ precision: Precision2.RANGE
1187
+ });
1188
+ }
1189
+ return new ProvisionResult3({
1190
+ command: `jq '${expr}' ${first.original}`,
1191
+ networkReadLow: fileSize,
1192
+ networkReadHigh: fileSize,
1193
+ readOps: 1,
1194
+ precision: Precision2.EXACT
1195
+ });
1196
+ } catch {
1197
+ return new ProvisionResult3({ command: "jq" });
1198
+ }
1199
+ }
1200
+ function formatResult(result, raw, compact) {
1201
+ if (raw && typeof result === "string") return ENC5.encode(result + "\n");
1202
+ const json = compact ? JSON.stringify(result) : JSON.stringify(result, null, 2);
1203
+ return ENC5.encode(json + "\n");
1204
+ }
1205
+ function formatResults(result, raw, compact, spread) {
1206
+ if (spread && Array.isArray(result)) {
1207
+ const parts = [];
1208
+ for (const item of result) parts.push(formatResult(item, raw, compact));
1209
+ return concat(parts);
1210
+ }
1211
+ return formatResult(result, raw, compact);
1212
+ }
1213
+ function concat(chunks) {
1214
+ let total = 0;
1215
+ for (const c of chunks) total += c.byteLength;
1216
+ const out = new Uint8Array(total);
1217
+ let offset = 0;
1218
+ for (const c of chunks) {
1219
+ out.set(c, offset);
1220
+ offset += c.byteLength;
1221
+ }
1222
+ return out;
1223
+ }
1224
+ async function readFile2(accessor, p) {
1225
+ return materialize2(stream(accessor, p));
1226
+ }
1227
+ async function jqCommand(accessor, paths, texts, opts) {
1228
+ const expression = texts[0];
1229
+ if (expression === void 0) {
1230
+ return [
1231
+ null,
1232
+ new IOResult10({ exitCode: 1, stderr: ENC5.encode("jq: usage: jq EXPRESSION [path]\n") })
1233
+ ];
1234
+ }
1235
+ const raw = opts.flags.r === true;
1236
+ const compact = opts.flags.c === true;
1237
+ const slurp = opts.flags.s === true;
1238
+ if (paths.length > 0) {
1239
+ const first = paths[0];
1240
+ if (first === void 0) return [null, new IOResult10()];
1241
+ if (isJsonlPath(first.original) && isStreamableJsonlExpr(expression)) {
1242
+ return [evalJsonlStream(stream(accessor, first), expression), new IOResult10()];
1243
+ }
1244
+ const outputs = [];
1245
+ for (const p of paths) {
1246
+ const bytes2 = await readFile2(accessor, p);
1247
+ let data2 = parseJsonPath(bytes2, p.original);
1248
+ if (slurp) data2 = Array.isArray(data2) ? data2 : [data2];
1249
+ const result2 = await jqEval(data2, expression.trim());
1250
+ const spread2 = expression.includes("[]");
1251
+ outputs.push(formatResults(result2, raw, compact, spread2));
1252
+ }
1253
+ const out = concat(outputs);
1254
+ return [out, new IOResult10()];
1255
+ }
1256
+ const bytes = await readStdinAsync(opts.stdin);
1257
+ if (bytes === null) return [null, new IOResult10()];
1258
+ let data = parseJsonAuto(bytes);
1259
+ if (slurp && !Array.isArray(data)) data = [data];
1260
+ const result = await jqEval(data, expression.trim());
1261
+ const spread = expression.includes("[]");
1262
+ return [formatResults(result, raw, compact, spread), new IOResult10()];
1263
+ }
1264
+ var SSH_JQ = command10({
1265
+ name: "jq",
1266
+ resource: ResourceName10.SSH,
1267
+ spec: specOf10("jq"),
1268
+ fn: jqCommand,
1269
+ provision: jqProvision
1270
+ });
1271
+
1272
+ // src/commands/builtin/ssh/ls/ls.ts
1273
+ import {
1274
+ FileType as FileType4,
1275
+ IOResult as IOResult11,
1276
+ PathSpec as PathSpec5,
1277
+ ResourceName as ResourceName11,
1278
+ command as command11,
1279
+ specOf as specOf11
1280
+ } from "@struktoai/mirage-core";
1281
+
1282
+ // src/core/ssh/readdir.ts
1283
+ async function readdir(accessor, p) {
1284
+ const sftp = await accessor.sftp();
1285
+ const virtual = p.pattern !== null ? p.directory.slice(p.prefix.length) || "/" : stripPrefix(p);
1286
+ const remote = joinRoot(accessor.config.root ?? "/", virtual);
1287
+ const list = await new Promise((resolveFn, rejectFn) => {
1288
+ sftp.readdir(remote, (err, entries) => {
1289
+ if (err !== void 0) {
1290
+ if (isNoSuchFile(err)) rejectFn(enoent(virtual));
1291
+ else rejectFn(err);
1292
+ return;
1293
+ }
1294
+ resolveFn(entries);
1295
+ });
1296
+ });
1297
+ const base = `/${virtual.replace(/^\/+|\/+$/g, "")}`;
1298
+ const dirPrefix = base === "/" ? "/" : `${base}/`;
1299
+ const mountPrefix = p.prefix;
1300
+ const names = [];
1301
+ for (const entry of list) {
1302
+ if (entry.filename === "." || entry.filename === "..") continue;
1303
+ names.push(`${mountPrefix}${dirPrefix}${entry.filename}`);
1304
+ }
1305
+ names.sort();
1306
+ return names;
1307
+ }
1308
+
1309
+ // src/commands/builtin/ssh/ls/ls.ts
1310
+ function humanSize2(n) {
1311
+ const units = ["B", "K", "M", "G", "T"];
1312
+ let v = n;
1313
+ let i = 0;
1314
+ while (v >= 1024 && i < units.length - 1) {
1315
+ v /= 1024;
1316
+ i += 1;
1317
+ }
1318
+ const s = v >= 10 || i === 0 ? Math.round(v).toString() : v.toFixed(1);
1319
+ return `${s}${units[i] ?? ""}`;
1320
+ }
1321
+ function childSpec(entryPath, prefix) {
1322
+ return new PathSpec5({
1323
+ original: entryPath,
1324
+ directory: entryPath,
1325
+ resolved: false,
1326
+ prefix
1327
+ });
1328
+ }
1329
+ function formatEntry(s, long, human, classify) {
1330
+ if (long) {
1331
+ const size = human ? humanSize2(s.size ?? 0) : String(s.size ?? 0);
1332
+ return `${s.type ?? "-"} ${size} ${s.modified ?? ""} ${s.name}`;
1333
+ }
1334
+ const suffix = classify && s.type === FileType4.DIRECTORY ? "/" : "";
1335
+ return `${s.name}${suffix}`;
1336
+ }
1337
+ function sortStats(stats, sortBy, reverse) {
1338
+ const sorted = [...stats].sort((a, b) => {
1339
+ if (sortBy === "time") return (b.modified ?? "").localeCompare(a.modified ?? "");
1340
+ if (sortBy === "size") return (b.size ?? 0) - (a.size ?? 0);
1341
+ return a.name.localeCompare(b.name);
1342
+ });
1343
+ if (reverse) sorted.reverse();
1344
+ return sorted;
1345
+ }
1346
+ async function listDir(accessor, dir, all) {
1347
+ const entries = await readdir(accessor, dir);
1348
+ const stats = await Promise.all(entries.map((p) => stat(accessor, childSpec(p, dir.prefix))));
1349
+ return all ? stats : stats.filter((s) => !s.name.startsWith("."));
1350
+ }
1351
+ async function walkRecursive(accessor, dir, opts, header, lines, warnings) {
1352
+ let stats;
1353
+ try {
1354
+ stats = await listDir(accessor, dir, opts.all);
1355
+ } catch (err) {
1356
+ const msg = err instanceof Error ? err.message : String(err);
1357
+ warnings.push(`ls: cannot access '${dir.original}': ${msg}`);
1358
+ return;
1359
+ }
1360
+ if (header) lines.push(`${dir.stripPrefix}:`);
1361
+ const sorted = sortStats(stats, opts.sortBy, opts.reverse);
1362
+ for (const s of sorted) lines.push(formatEntry(s, opts.long, opts.human, opts.classify));
1363
+ const subdirs = sorted.filter((s) => s.type === FileType4.DIRECTORY);
1364
+ for (const sub of subdirs) {
1365
+ lines.push("");
1366
+ const base = dir.stripPrefix.replace(/\/+$/, "");
1367
+ const childPath = `${base}/${sub.name}`;
1368
+ await walkRecursive(accessor, childSpec(childPath, dir.prefix), opts, true, lines, warnings);
1369
+ }
1370
+ }
1371
+ async function lsCommand(accessor, paths, _texts, opts) {
1372
+ const targets = paths.length > 0 ? paths : [
1373
+ new PathSpec5({
1374
+ original: opts.cwd,
1375
+ directory: opts.cwd,
1376
+ resolved: false,
1377
+ prefix: opts.mountPrefix ?? ""
1378
+ })
1379
+ ];
1380
+ const long = opts.flags.args_l === true;
1381
+ const all = opts.flags.a === true || opts.flags.A === true;
1382
+ const human = opts.flags.h === true;
1383
+ const reverse = opts.flags.r === true;
1384
+ const classify = opts.flags.F === true;
1385
+ const recursive = opts.flags.R === true;
1386
+ const listDirItself = opts.flags.d === true;
1387
+ const sortBy = opts.flags.t === true ? "time" : opts.flags.S === true ? "size" : "name";
1388
+ const warnings = [];
1389
+ const lines = [];
1390
+ if (listDirItself) {
1391
+ for (const p of targets) {
1392
+ try {
1393
+ const s = await stat(accessor, p);
1394
+ lines.push(formatEntry(s, long, human, classify));
1395
+ } catch (err) {
1396
+ const msg = err instanceof Error ? err.message : String(err);
1397
+ warnings.push(`ls: cannot access '${p.original}': ${msg}`);
1398
+ }
1399
+ }
1400
+ const out2 = new TextEncoder().encode(lines.join("\n"));
1401
+ const exitCode2 = warnings.length > 0 && lines.length === 0 ? 1 : 0;
1402
+ if (warnings.length > 0) {
1403
+ const stderr = new TextEncoder().encode(warnings.join("\n"));
1404
+ return [out2, new IOResult11({ stderr, exitCode: exitCode2 })];
1405
+ }
1406
+ return [out2, new IOResult11({ exitCode: exitCode2 })];
1407
+ }
1408
+ if (recursive) {
1409
+ const walkOpts = { all, long, human, classify, sortBy, reverse };
1410
+ for (let i = 0; i < targets.length; i++) {
1411
+ const p = targets[i];
1412
+ if (p === void 0) continue;
1413
+ if (i > 0) lines.push("");
1414
+ await walkRecursive(accessor, p, walkOpts, true, lines, warnings);
1415
+ }
1416
+ const out2 = new TextEncoder().encode(lines.join("\n"));
1417
+ const exitCode2 = warnings.length > 0 && lines.length === 0 ? 1 : 0;
1418
+ if (warnings.length > 0) {
1419
+ const stderr = new TextEncoder().encode(warnings.join("\n"));
1420
+ return [out2, new IOResult11({ stderr, exitCode: exitCode2 })];
1421
+ }
1422
+ return [out2, new IOResult11({ exitCode: exitCode2 })];
1423
+ }
1424
+ for (const p of targets) {
1425
+ let stats;
1426
+ try {
1427
+ stats = await listDir(accessor, p, all);
1428
+ } catch (err) {
1429
+ try {
1430
+ stats = [await stat(accessor, p)];
1431
+ } catch {
1432
+ const msg = err instanceof Error ? err.message : String(err);
1433
+ warnings.push(`ls: cannot access '${p.original}': ${msg}`);
1434
+ continue;
1435
+ }
1436
+ }
1437
+ for (const s of sortStats(stats, sortBy, reverse))
1438
+ lines.push(formatEntry(s, long, human, classify));
1439
+ }
1440
+ const out = new TextEncoder().encode(lines.join("\n"));
1441
+ const exitCode = warnings.length > 0 && lines.length === 0 ? 1 : 0;
1442
+ if (warnings.length > 0) {
1443
+ const stderr = new TextEncoder().encode(warnings.join("\n"));
1444
+ return [out, new IOResult11({ stderr, exitCode })];
1445
+ }
1446
+ return [out, new IOResult11({ exitCode })];
1447
+ }
1448
+ var SSH_LS = command11({
1449
+ name: "ls",
1450
+ resource: ResourceName11.SSH,
1451
+ spec: specOf11("ls"),
1452
+ fn: lsCommand
1453
+ });
1454
+
1455
+ // src/commands/builtin/ssh/mkdir.ts
1456
+ import {
1457
+ IOResult as IOResult12,
1458
+ ResourceName as ResourceName12,
1459
+ command as command12,
1460
+ specOf as specOf12
1461
+ } from "@struktoai/mirage-core";
1462
+
1463
+ // src/core/ssh/mkdir.ts
1464
+ async function statRemote2(sftp, remote) {
1465
+ return new Promise((resolveFn) => {
1466
+ sftp.lstat(remote, (err, stats) => {
1467
+ if (err) resolveFn(null);
1468
+ else resolveFn(stats);
1469
+ });
1470
+ });
1471
+ }
1472
+ async function mkdirOne(sftp, remote, ignoreExisting) {
1473
+ return new Promise((resolveFn, rejectFn) => {
1474
+ sftp.mkdir(remote, (err) => {
1475
+ if (!err) {
1476
+ resolveFn();
1477
+ return;
1478
+ }
1479
+ if (ignoreExisting) {
1480
+ resolveFn();
1481
+ return;
1482
+ }
1483
+ rejectFn(err);
1484
+ });
1485
+ });
1486
+ }
1487
+ async function mkdir(accessor, p, recursive) {
1488
+ const sftp = await accessor.sftp();
1489
+ const virtual = stripPrefix(p);
1490
+ const root = accessor.config.root ?? "/";
1491
+ const remote = joinRoot(root, virtual);
1492
+ if (!recursive) {
1493
+ try {
1494
+ await mkdirOne(sftp, remote, false);
1495
+ } catch (err) {
1496
+ if (isNoSuchFile(err)) throw enoent(virtual);
1497
+ throw err;
1498
+ }
1499
+ return;
1500
+ }
1501
+ const cleaned = virtual.replace(/^\/+|\/+$/g, "");
1502
+ if (cleaned.length === 0) return;
1503
+ const parts = cleaned.split("/");
1504
+ let cur = "";
1505
+ for (const part of parts) {
1506
+ cur = cur === "" ? `/${part}` : `${cur}/${part}`;
1507
+ const stepRemote = joinRoot(root, cur);
1508
+ const existing = await statRemote2(sftp, stepRemote);
1509
+ if (existing?.isDirectory()) continue;
1510
+ await mkdirOne(sftp, stepRemote, true);
1511
+ }
1512
+ }
1513
+
1514
+ // src/commands/builtin/ssh/mkdir.ts
1515
+ async function mkdirCommand(accessor, paths, _texts, opts) {
1516
+ const parents = opts.flags.p === true;
1517
+ const verbose = opts.flags.v === true;
1518
+ if (paths.length === 0) {
1519
+ return [
1520
+ null,
1521
+ new IOResult12({
1522
+ exitCode: 1,
1523
+ stderr: new TextEncoder().encode("mkdir: missing operand\n")
1524
+ })
1525
+ ];
1526
+ }
1527
+ const lines = [];
1528
+ for (const p of paths) {
1529
+ await mkdir(accessor, p, parents);
1530
+ if (verbose) lines.push(`mkdir: created directory '${p.original}'`);
1531
+ }
1532
+ const out = lines.length > 0 ? new TextEncoder().encode(lines.join("\n") + "\n") : null;
1533
+ return [out, new IOResult12()];
1534
+ }
1535
+ var SSH_MKDIR = command12({
1536
+ name: "mkdir",
1537
+ resource: ResourceName12.SSH,
1538
+ spec: specOf12("mkdir"),
1539
+ fn: mkdirCommand,
1540
+ write: true
1541
+ });
1542
+
1543
+ // src/commands/builtin/ssh/mv.ts
1544
+ import {
1545
+ IOResult as IOResult13,
1546
+ ResourceName as ResourceName13,
1547
+ command as command13,
1548
+ specOf as specOf13
1549
+ } from "@struktoai/mirage-core";
1550
+
1551
+ // src/core/ssh/rename.ts
1552
+ async function rename(accessor, src, dst) {
1553
+ const sftp = await accessor.sftp();
1554
+ const root = accessor.config.root ?? "/";
1555
+ const srcVirtual = stripPrefix(src);
1556
+ const dstVirtual = stripPrefix(dst);
1557
+ const remoteSrc = joinRoot(root, srcVirtual);
1558
+ const remoteDst = joinRoot(root, dstVirtual);
1559
+ await new Promise((resolveFn, rejectFn) => {
1560
+ sftp.rename(remoteSrc, remoteDst, (err) => {
1561
+ if (!err) {
1562
+ resolveFn();
1563
+ return;
1564
+ }
1565
+ if (isNoSuchFile(err)) rejectFn(enoent(srcVirtual));
1566
+ else rejectFn(err);
1567
+ });
1568
+ });
1569
+ }
1570
+
1571
+ // src/commands/builtin/ssh/mv.ts
1572
+ async function exists2(accessor, path) {
1573
+ try {
1574
+ await stat(accessor, path);
1575
+ return true;
1576
+ } catch {
1577
+ return false;
1578
+ }
1579
+ }
1580
+ async function mvCommand(accessor, paths, _texts, opts) {
1581
+ if (paths.length < 2) {
1582
+ return [
1583
+ null,
1584
+ new IOResult13({
1585
+ exitCode: 1,
1586
+ stderr: new TextEncoder().encode("mv: missing operand\n")
1587
+ })
1588
+ ];
1589
+ }
1590
+ const noClobber = opts.flags.n === true;
1591
+ const verbose = opts.flags.v === true;
1592
+ const sources = paths.slice(0, -1);
1593
+ const dst = paths[paths.length - 1];
1594
+ if (dst === void 0) return [null, new IOResult13()];
1595
+ const lines = [];
1596
+ for (const src of sources) {
1597
+ if (noClobber && await exists2(accessor, dst)) continue;
1598
+ await rename(accessor, src, dst);
1599
+ if (verbose) lines.push(`'${src.original}' -> '${dst.original}'`);
1600
+ }
1601
+ const out = lines.length > 0 ? new TextEncoder().encode(lines.join("\n") + "\n") : null;
1602
+ return [out, new IOResult13()];
1603
+ }
1604
+ var SSH_MV = command13({
1605
+ name: "mv",
1606
+ resource: ResourceName13.SSH,
1607
+ spec: specOf13("mv"),
1608
+ fn: mvCommand,
1609
+ write: true
1610
+ });
1611
+
1612
+ // src/commands/builtin/ssh/realpath.ts
1613
+ import {
1614
+ IOResult as IOResult14,
1615
+ ResourceName as ResourceName14,
1616
+ command as command14,
1617
+ specOf as specOf14
1618
+ } from "@struktoai/mirage-core";
1619
+ function normalize(p, cwd) {
1620
+ const path = p.startsWith("/") ? p : `${cwd.replace(/\/+$/, "")}/${p}`;
1621
+ const parts = path.split("/").filter((s) => s !== "" && s !== ".");
1622
+ const out = [];
1623
+ for (const part of parts) {
1624
+ if (part === "..") out.pop();
1625
+ else out.push(part);
1626
+ }
1627
+ return "/" + out.join("/");
1628
+ }
1629
+ async function existsSsh(accessor, p) {
1630
+ try {
1631
+ await stat(accessor, p);
1632
+ return true;
1633
+ } catch {
1634
+ return false;
1635
+ }
1636
+ }
1637
+ async function realpathCommand(accessor, paths, texts, opts) {
1638
+ const requireExists = opts.flags.e === true;
1639
+ const lines = [];
1640
+ if (paths.length > 0) {
1641
+ for (const p of paths) {
1642
+ if (requireExists && !await existsSsh(accessor, p)) {
1643
+ throw new Error(`realpath: '${p.original}': No such file or directory`);
1644
+ }
1645
+ lines.push(normalize(p.original, opts.cwd));
1646
+ }
1647
+ } else {
1648
+ for (const t of texts) lines.push(normalize(t, opts.cwd));
1649
+ }
1650
+ const out = new TextEncoder().encode(lines.join("\n") + "\n");
1651
+ return [out, new IOResult14()];
1652
+ }
1653
+ var SSH_REALPATH = command14({
1654
+ name: "realpath",
1655
+ resource: ResourceName14.SSH,
1656
+ spec: specOf14("realpath"),
1657
+ fn: realpathCommand
1658
+ });
1659
+
1660
+ // src/commands/builtin/ssh/rg.ts
1661
+ import {
1662
+ FileType as FileType5,
1663
+ IOResult as IOResult15,
1664
+ PathSpec as PathSpec6,
1665
+ ResourceName as ResourceName15,
1666
+ command as command15,
1667
+ compilePattern as compilePattern2,
1668
+ exitOnEmpty as exitOnEmpty2,
1669
+ grepStream as grepStream2,
1670
+ materialize as materialize3,
1671
+ resolveSource as resolveSource4,
1672
+ rgFolderFiletype,
1673
+ rgFull,
1674
+ specOf as specOf15
1675
+ } from "@struktoai/mirage-core";
1676
+ var ENC6 = new TextEncoder();
1677
+ function parseRgFlags(flags) {
1678
+ const toInt = (v) => typeof v === "string" ? Number.parseInt(v, 10) : null;
1679
+ const a = toInt(flags.A);
1680
+ const b = toInt(flags.B);
1681
+ const c = toInt(flags.C);
1682
+ return {
1683
+ ignoreCase: flags.i === true,
1684
+ invert: flags.v === true,
1685
+ lineNumbers: flags.n === true,
1686
+ countOnly: flags.c === true,
1687
+ filesOnly: flags.args_l === true,
1688
+ wholeWord: flags.w === true,
1689
+ fixedString: flags.F === true,
1690
+ onlyMatching: flags.o === true,
1691
+ maxCount: toInt(flags.m),
1692
+ afterContext: a ?? c ?? 0,
1693
+ beforeContext: b ?? c ?? 0,
1694
+ fileType: typeof flags.type === "string" ? flags.type : null,
1695
+ globPattern: typeof flags.glob === "string" ? flags.glob : null,
1696
+ hidden: flags.hidden === true
1697
+ };
1698
+ }
1699
+ async function rgCommand(accessor, paths, texts, opts) {
1700
+ const [exprText] = texts;
1701
+ if (exprText === void 0) {
1702
+ return [
1703
+ null,
1704
+ new IOResult15({ exitCode: 2, stderr: ENC6.encode("rg: usage: rg [flags] pattern [path]\n") })
1705
+ ];
1706
+ }
1707
+ const flags = parseRgFlags(opts.flags);
1708
+ const [first] = paths;
1709
+ if (first === void 0) {
1710
+ const source = resolveSource4(opts.stdin, "rg: usage: rg [flags] pattern [path]");
1711
+ const pat = compilePattern2(exprText, flags.ignoreCase, flags.fixedString, flags.wholeWord);
1712
+ const stream2 = grepStream2(source, pat, {
1713
+ invert: flags.invert,
1714
+ lineNumbers: flags.lineNumbers,
1715
+ countOnly: flags.countOnly,
1716
+ onlyMatching: flags.onlyMatching,
1717
+ maxCount: flags.maxCount,
1718
+ afterContext: flags.afterContext,
1719
+ beforeContext: flags.beforeContext
1720
+ });
1721
+ const io = new IOResult15();
1722
+ return [exitOnEmpty2(stream2, io), io];
1723
+ }
1724
+ let isDir = false;
1725
+ try {
1726
+ const s = await stat(accessor, first);
1727
+ isDir = s.type === FileType5.DIRECTORY;
1728
+ } catch {
1729
+ try {
1730
+ await readdir(accessor, first);
1731
+ isDir = true;
1732
+ } catch {
1733
+ }
1734
+ }
1735
+ if (isDir && opts.filetypeFns !== null && Object.keys(opts.filetypeFns).length > 0) {
1736
+ const readdirFn = async (p) => {
1737
+ const keys = await find(accessor, makeSpec(p, first), { type: null });
1738
+ return keys;
1739
+ };
1740
+ const statFn = (p) => stat(accessor, makeSpec(p, first));
1741
+ const readBytesFn = (p) => materialize3(stream(accessor, makeSpec(p, first)));
1742
+ const warnings = [];
1743
+ const results = await rgFolderFiletype(
1744
+ readdirFn,
1745
+ statFn,
1746
+ readBytesFn,
1747
+ first.original,
1748
+ exprText,
1749
+ {},
1750
+ {
1751
+ ignoreCase: flags.ignoreCase,
1752
+ invert: flags.invert,
1753
+ lineNumbers: flags.lineNumbers,
1754
+ countOnly: flags.countOnly,
1755
+ filesOnly: flags.filesOnly,
1756
+ onlyMatching: flags.onlyMatching,
1757
+ maxCount: flags.maxCount,
1758
+ fixedString: flags.fixedString,
1759
+ wholeWord: flags.wholeWord,
1760
+ fileType: flags.fileType,
1761
+ globPattern: flags.globPattern,
1762
+ hidden: flags.hidden
1763
+ },
1764
+ warnings
1765
+ );
1766
+ const stderr = warnings.length > 0 ? ENC6.encode(warnings.join("\n")) : void 0;
1767
+ if (results.length === 0) {
1768
+ const io2 = new IOResult15({ exitCode: 1, ...stderr !== void 0 ? { stderr } : {} });
1769
+ return [new Uint8Array(0), io2];
1770
+ }
1771
+ const out = ENC6.encode(results.join("\n"));
1772
+ const io = new IOResult15(stderr !== void 0 ? { stderr } : {});
1773
+ return [out, io];
1774
+ }
1775
+ const needsFull = flags.filesOnly || flags.beforeContext > 0 || flags.afterContext > 0 || flags.fileType !== null || flags.globPattern !== null;
1776
+ if (needsFull) {
1777
+ const readdirFn = async (p) => {
1778
+ const keys = await find(accessor, makeSpec(p, first), { type: null });
1779
+ return keys;
1780
+ };
1781
+ const statFn = (p) => stat(accessor, makeSpec(p, first));
1782
+ const readBytesFn = (p) => materialize3(stream(accessor, makeSpec(p, first)));
1783
+ const warnings = [];
1784
+ const results = await rgFull(
1785
+ readdirFn,
1786
+ statFn,
1787
+ readBytesFn,
1788
+ first.original,
1789
+ exprText,
1790
+ {
1791
+ ignoreCase: flags.ignoreCase,
1792
+ invert: flags.invert,
1793
+ lineNumbers: flags.lineNumbers,
1794
+ countOnly: flags.countOnly,
1795
+ filesOnly: flags.filesOnly,
1796
+ fixedString: flags.fixedString,
1797
+ onlyMatching: flags.onlyMatching,
1798
+ maxCount: flags.maxCount,
1799
+ wholeWord: flags.wholeWord,
1800
+ contextBefore: flags.beforeContext,
1801
+ contextAfter: flags.afterContext,
1802
+ fileType: flags.fileType,
1803
+ globPattern: flags.globPattern,
1804
+ hidden: flags.hidden
1805
+ },
1806
+ warnings
1807
+ );
1808
+ const stderr = warnings.length > 0 ? ENC6.encode(warnings.join("\n")) : void 0;
1809
+ if (results.length === 0) {
1810
+ const io2 = new IOResult15({ exitCode: 1, ...stderr !== void 0 ? { stderr } : {} });
1811
+ return [new Uint8Array(0), io2];
1812
+ }
1813
+ const out = ENC6.encode(results.join("\n"));
1814
+ const io = new IOResult15(stderr !== void 0 ? { stderr } : {});
1815
+ return [out, io];
1816
+ }
1817
+ return grepImpl("rg", accessor, paths, texts, opts);
1818
+ }
1819
+ function makeSpec(path, template) {
1820
+ return new PathSpec6({
1821
+ original: path,
1822
+ directory: path,
1823
+ resolved: false,
1824
+ prefix: template.prefix
1825
+ });
1826
+ }
1827
+ var SSH_RG = command15({
1828
+ name: "rg",
1829
+ resource: ResourceName15.SSH,
1830
+ spec: specOf15("rg"),
1831
+ fn: rgCommand
1832
+ });
1833
+
1834
+ // src/commands/builtin/ssh/rm.ts
1835
+ import {
1836
+ FileType as FileType6,
1837
+ IOResult as IOResult16,
1838
+ ResourceName as ResourceName16,
1839
+ command as command16,
1840
+ specOf as specOf16
1841
+ } from "@struktoai/mirage-core";
1842
+
1843
+ // src/core/ssh/rmdir.ts
1844
+ async function rmdir(accessor, p) {
1845
+ const sftp = await accessor.sftp();
1846
+ const virtual = stripPrefix(p);
1847
+ const remote = joinRoot(accessor.config.root ?? "/", virtual);
1848
+ await new Promise((resolveFn, rejectFn) => {
1849
+ sftp.rmdir(remote, (err) => {
1850
+ if (!err) {
1851
+ resolveFn();
1852
+ return;
1853
+ }
1854
+ if (isNoSuchFile(err)) rejectFn(enoent(virtual));
1855
+ else rejectFn(err);
1856
+ });
1857
+ });
1858
+ }
1859
+
1860
+ // src/core/ssh/unlink.ts
1861
+ async function unlink(accessor, p) {
1862
+ const sftp = await accessor.sftp();
1863
+ const virtual = stripPrefix(p);
1864
+ const remote = joinRoot(accessor.config.root ?? "/", virtual);
1865
+ await new Promise((resolveFn, rejectFn) => {
1866
+ sftp.unlink(remote, (err) => {
1867
+ if (!err) {
1868
+ resolveFn();
1869
+ return;
1870
+ }
1871
+ if (isNoSuchFile(err)) rejectFn(enoent(virtual));
1872
+ else rejectFn(err);
1873
+ });
1874
+ });
1875
+ }
1876
+
1877
+ // src/core/ssh/rm.ts
1878
+ async function statRemote3(sftp, remote) {
1879
+ return new Promise((resolveFn, rejectFn) => {
1880
+ sftp.lstat(remote, (err, stats) => {
1881
+ if (err !== void 0) rejectFn(err);
1882
+ else resolveFn(stats);
1883
+ });
1884
+ });
1885
+ }
1886
+ async function readdirRemote(sftp, remote) {
1887
+ return new Promise((resolveFn, rejectFn) => {
1888
+ sftp.readdir(remote, (err, list) => {
1889
+ if (err !== void 0) rejectFn(err);
1890
+ else resolveFn(list);
1891
+ });
1892
+ });
1893
+ }
1894
+ async function unlinkRemote(sftp, remote) {
1895
+ return new Promise((resolveFn, rejectFn) => {
1896
+ sftp.unlink(remote, (err) => {
1897
+ if (err) rejectFn(err);
1898
+ else resolveFn();
1899
+ });
1900
+ });
1901
+ }
1902
+ async function rmdirRemote(sftp, remote) {
1903
+ return new Promise((resolveFn, rejectFn) => {
1904
+ sftp.rmdir(remote, (err) => {
1905
+ if (err) rejectFn(err);
1906
+ else resolveFn();
1907
+ });
1908
+ });
1909
+ }
1910
+ async function rmRecurse(sftp, remote) {
1911
+ const stats = await statRemote3(sftp, remote);
1912
+ if (isDirectoryAttrs(stats)) {
1913
+ const entries = await readdirRemote(sftp, remote);
1914
+ for (const entry of entries) {
1915
+ if (entry.filename === "." || entry.filename === "..") continue;
1916
+ const child = remote === "/" ? `/${entry.filename}` : `${remote}/${entry.filename}`;
1917
+ await rmRecurse(sftp, child);
1918
+ }
1919
+ await rmdirRemote(sftp, remote);
1920
+ return;
1921
+ }
1922
+ await unlinkRemote(sftp, remote);
1923
+ }
1924
+ async function rmR(accessor, p) {
1925
+ const sftp = await accessor.sftp();
1926
+ const virtual = stripPrefix(p);
1927
+ const remote = joinRoot(accessor.config.root ?? "/", virtual);
1928
+ try {
1929
+ await rmRecurse(sftp, remote);
1930
+ } catch (err) {
1931
+ if (isNoSuchFile(err)) throw enoent(virtual);
1932
+ throw err;
1933
+ }
1934
+ }
1935
+
1936
+ // src/commands/builtin/ssh/rm.ts
1937
+ async function rmCommand(accessor, paths, _texts, opts) {
1938
+ if (paths.length === 0) {
1939
+ return [
1940
+ null,
1941
+ new IOResult16({
1942
+ exitCode: 1,
1943
+ stderr: new TextEncoder().encode("rm: missing operand\n")
1944
+ })
1945
+ ];
1946
+ }
1947
+ const recursive = opts.flags.r === true || opts.flags.R === true;
1948
+ const force = opts.flags.f === true;
1949
+ const verbose = opts.flags.v === true;
1950
+ const lines = [];
1951
+ for (const p of paths) {
1952
+ let isDir = false;
1953
+ try {
1954
+ const st = await stat(accessor, p);
1955
+ isDir = st.type === FileType6.DIRECTORY;
1956
+ } catch {
1957
+ if (force) continue;
1958
+ throw new Error(`rm: cannot remove '${p.original}': No such file or directory`);
1959
+ }
1960
+ if (isDir) {
1961
+ if (recursive) {
1962
+ await rmR(accessor, p);
1963
+ } else {
1964
+ await rmdir(accessor, p);
1965
+ }
1966
+ } else {
1967
+ await unlink(accessor, p);
1968
+ }
1969
+ if (verbose) lines.push(`removed '${p.original}'`);
1970
+ }
1971
+ const out = lines.length > 0 ? new TextEncoder().encode(lines.join("\n")) : null;
1972
+ return [out, new IOResult16()];
1973
+ }
1974
+ var SSH_RM = command16({
1975
+ name: "rm",
1976
+ resource: ResourceName16.SSH,
1977
+ spec: specOf16("rm"),
1978
+ fn: rmCommand,
1979
+ write: true
1980
+ });
1981
+
1982
+ // src/commands/builtin/ssh/stat/stat.ts
1983
+ import {
1984
+ FileType as FileType7,
1985
+ IOResult as IOResult17,
1986
+ ProvisionResult as ProvisionResult4,
1987
+ ResourceName as ResourceName17,
1988
+ command as command17,
1989
+ specOf as specOf17
1990
+ } from "@struktoai/mirage-core";
1991
+ var TYPE_LABELS = {
1992
+ [FileType7.DIRECTORY]: "directory",
1993
+ [FileType7.TEXT]: "regular file",
1994
+ [FileType7.BINARY]: "regular file",
1995
+ [FileType7.JSON]: "regular file",
1996
+ [FileType7.CSV]: "regular file"
1997
+ };
1998
+ function formatStat(fmt, s) {
1999
+ return fmt.replace(/%(.)/g, (_, spec) => {
2000
+ if (spec === "n") return s.name;
2001
+ if (spec === "s") return String(s.size ?? 0);
2002
+ if (spec === "F") return s.type ? TYPE_LABELS[s.type] ?? "regular file" : "regular file";
2003
+ if (spec === "y") return s.modified ?? "";
2004
+ return "?";
2005
+ });
2006
+ }
2007
+ function statProvision(accessor, paths, _texts, _opts) {
2008
+ const [first] = paths;
2009
+ return new ProvisionResult4({
2010
+ command: first !== void 0 ? `stat ${first.original}` : "stat"
2011
+ });
2012
+ }
2013
+ async function statCommand(accessor, paths, texts, opts) {
2014
+ if (paths.length === 0) {
2015
+ return [
2016
+ null,
2017
+ new IOResult17({
2018
+ exitCode: 1,
2019
+ stderr: new TextEncoder().encode("stat: missing operand\n")
2020
+ })
2021
+ ];
2022
+ }
2023
+ const fmt = typeof opts.flags.c === "string" ? opts.flags.c : typeof opts.flags.f === "string" ? opts.flags.f : null;
2024
+ const lines = [];
2025
+ for (const p of paths) {
2026
+ const s = await stat(accessor, p);
2027
+ if (fmt !== null) {
2028
+ lines.push(formatStat(fmt, s));
2029
+ } else {
2030
+ const sizeStr = s.size === null ? "None" : String(s.size);
2031
+ const modStr = s.modified ?? "None";
2032
+ const typeStr = s.type ?? "None";
2033
+ lines.push(`name=${s.name} size=${sizeStr} modified=${modStr} type=${typeStr}`);
2034
+ }
2035
+ }
2036
+ const out = new TextEncoder().encode(lines.join("\n"));
2037
+ return [out, new IOResult17()];
2038
+ }
2039
+ var SSH_STAT = command17({
2040
+ name: "stat",
2041
+ resource: ResourceName17.SSH,
2042
+ spec: specOf17("stat"),
2043
+ fn: statCommand,
2044
+ provision: statProvision
2045
+ });
2046
+
2047
+ // src/commands/builtin/ssh/tail/tail.ts
2048
+ import {
2049
+ IOResult as IOResult18,
2050
+ ResourceName as ResourceName18,
2051
+ command as command18,
2052
+ countNewlines,
2053
+ headerAggregate as headerAggregate2,
2054
+ materialize as materialize4,
2055
+ parseN,
2056
+ readStdinAsync as readStdinAsync2,
2057
+ specOf as specOf18,
2058
+ tailBytes
2059
+ } from "@struktoai/mirage-core";
2060
+ var ENC7 = new TextEncoder();
2061
+ async function readFile3(accessor, p) {
2062
+ return materialize4(stream(accessor, p));
2063
+ }
2064
+ async function tailCommand(accessor, paths, texts, opts) {
2065
+ const nRaw = typeof opts.flags.n === "string" ? opts.flags.n : null;
2066
+ const cRaw = typeof opts.flags.c === "string" ? opts.flags.c : null;
2067
+ const qFlag = opts.flags.q === true;
2068
+ const vFlag = opts.flags.v === true;
2069
+ const [lines, plusMode] = parseN(nRaw);
2070
+ const bytesMode = cRaw !== null ? Number.parseInt(cRaw, 10) : null;
2071
+ if (paths.length > 0) {
2072
+ const chunks = [];
2073
+ const cache = [];
2074
+ const showHeaders = (vFlag || paths.length > 1) && !qFlag;
2075
+ for (let i = 0; i < paths.length; i++) {
2076
+ const p = paths[i];
2077
+ if (p === void 0) continue;
2078
+ const raw2 = await readFile3(accessor, p);
2079
+ if (showHeaders) {
2080
+ const header = i > 0 ? `
2081
+ ==> ${p.original} <==
2082
+ ` : `==> ${p.original} <==
2083
+ `;
2084
+ chunks.push(ENC7.encode(header));
2085
+ }
2086
+ if (bytesMode !== null) {
2087
+ chunks.push(bytesMode === 0 ? new Uint8Array(0) : raw2.slice(-bytesMode));
2088
+ if (bytesMode >= raw2.byteLength) cache.push(p.original);
2089
+ } else {
2090
+ chunks.push(tailBytes(raw2, lines, null, plusMode));
2091
+ if (!plusMode && lines >= countNewlines(raw2)) cache.push(p.original);
2092
+ }
2093
+ }
2094
+ const out = concat2(chunks);
2095
+ return [out, new IOResult18({ cache })];
2096
+ }
2097
+ const raw = await readStdinAsync2(opts.stdin);
2098
+ if (raw === null) {
2099
+ return [null, new IOResult18({ exitCode: 1, stderr: ENC7.encode("tail: missing operand\n") })];
2100
+ }
2101
+ if (bytesMode !== null) {
2102
+ const out = bytesMode === 0 ? new Uint8Array(0) : raw.slice(-bytesMode);
2103
+ return [out, new IOResult18()];
2104
+ }
2105
+ return [tailBytes(raw, lines, null, plusMode), new IOResult18()];
2106
+ }
2107
+ function concat2(chunks) {
2108
+ let total = 0;
2109
+ for (const c of chunks) total += c.byteLength;
2110
+ const out = new Uint8Array(total);
2111
+ let offset = 0;
2112
+ for (const c of chunks) {
2113
+ out.set(c, offset);
2114
+ offset += c.byteLength;
2115
+ }
2116
+ return out;
2117
+ }
2118
+ var SSH_TAIL = command18({
2119
+ name: "tail",
2120
+ resource: ResourceName18.SSH,
2121
+ spec: specOf18("tail"),
2122
+ fn: tailCommand,
2123
+ aggregate: headerAggregate2
2124
+ });
2125
+
2126
+ // src/commands/builtin/ssh/touch.ts
2127
+ import {
2128
+ IOResult as IOResult19,
2129
+ ResourceName as ResourceName19,
2130
+ command as command19,
2131
+ specOf as specOf19
2132
+ } from "@struktoai/mirage-core";
2133
+
2134
+ // src/core/ssh/exists.ts
2135
+ async function exists3(accessor, p) {
2136
+ const sftp = await accessor.sftp();
2137
+ const remote = joinRoot(accessor.config.root ?? "/", stripPrefix(p));
2138
+ return new Promise((resolveFn) => {
2139
+ sftp.exists(remote, (yes) => {
2140
+ resolveFn(yes);
2141
+ });
2142
+ });
2143
+ }
2144
+
2145
+ // src/commands/builtin/ssh/touch.ts
2146
+ var ENC8 = new TextEncoder();
2147
+ async function touchCommand(accessor, paths, texts, opts) {
2148
+ if (paths.length === 0) {
2149
+ return [null, new IOResult19({ exitCode: 1, stderr: ENC8.encode("touch: missing operand\n") })];
2150
+ }
2151
+ const createOnly = opts.flags.c === true;
2152
+ for (const p of paths) {
2153
+ if (createOnly) continue;
2154
+ if (!await exists3(accessor, p)) {
2155
+ await writeBytes(accessor, p, new Uint8Array(0));
2156
+ }
2157
+ }
2158
+ return [null, new IOResult19()];
2159
+ }
2160
+ var SSH_TOUCH = command19({
2161
+ name: "touch",
2162
+ resource: ResourceName19.SSH,
2163
+ spec: specOf19("touch"),
2164
+ fn: touchCommand,
2165
+ write: true
2166
+ });
2167
+
2168
+ // src/commands/builtin/ssh/tree.ts
2169
+ import {
2170
+ FileType as FileType8,
2171
+ IOResult as IOResult20,
2172
+ PathSpec as PathSpec7,
2173
+ ResourceName as ResourceName20,
2174
+ command as command20,
2175
+ specOf as specOf20
2176
+ } from "@struktoai/mirage-core";
2177
+ function fnmatch2(name, pattern) {
2178
+ let re = "^";
2179
+ for (const ch of pattern) {
2180
+ if (ch === "*") re += ".*";
2181
+ else if (ch === "?") re += ".";
2182
+ else if (/[.+^${}()|[\]\\]/.test(ch)) re += "\\" + ch;
2183
+ else re += ch;
2184
+ }
2185
+ re += "$";
2186
+ return new RegExp(re).test(name);
2187
+ }
2188
+ async function walkTree(accessor, path, prefix, lines, treeOpts, depth) {
2189
+ let entries;
2190
+ try {
2191
+ entries = await readdir(accessor, path);
2192
+ } catch {
2193
+ return;
2194
+ }
2195
+ entries.sort();
2196
+ const filtered = [];
2197
+ for (const childPath of entries) {
2198
+ const name = childPath.slice(childPath.lastIndexOf("/") + 1);
2199
+ if (!treeOpts.showHidden && name.startsWith(".")) continue;
2200
+ if (treeOpts.ignorePattern !== null && fnmatch2(name, treeOpts.ignorePattern)) continue;
2201
+ const sub = new PathSpec7({
2202
+ original: childPath,
2203
+ directory: childPath,
2204
+ resolved: false,
2205
+ prefix: path.prefix
2206
+ });
2207
+ let isDir;
2208
+ try {
2209
+ const s = await stat(accessor, sub);
2210
+ isDir = s.type === FileType8.DIRECTORY;
2211
+ } catch {
2212
+ continue;
2213
+ }
2214
+ if (treeOpts.dirsOnly && !isDir) continue;
2215
+ if (treeOpts.matchPattern !== null && !isDir && !fnmatch2(name, treeOpts.matchPattern)) continue;
2216
+ filtered.push({ spec: sub, name, isDir });
2217
+ }
2218
+ for (let i = 0; i < filtered.length; i++) {
2219
+ const entry = filtered[i];
2220
+ if (entry === void 0) continue;
2221
+ const last = i === filtered.length - 1;
2222
+ const connector = last ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
2223
+ lines.push(`${prefix}${connector}${entry.name}`);
2224
+ if (entry.isDir) {
2225
+ if (treeOpts.maxDepth !== null && depth >= treeOpts.maxDepth) continue;
2226
+ const nextPrefix = prefix + (last ? " " : "\u2502 ");
2227
+ await walkTree(accessor, entry.spec, nextPrefix, lines, treeOpts, depth + 1);
2228
+ }
2229
+ }
2230
+ }
2231
+ async function treeCommand(accessor, paths, texts, opts) {
2232
+ const targets = paths.length > 0 ? paths : [
2233
+ new PathSpec7({
2234
+ original: opts.cwd,
2235
+ directory: opts.cwd,
2236
+ resolved: false,
2237
+ prefix: opts.mountPrefix ?? ""
2238
+ })
2239
+ ];
2240
+ const depthRaw = typeof opts.flags.L === "string" ? opts.flags.L : null;
2241
+ const ignoreRaw = typeof opts.flags.args_I === "string" ? opts.flags.args_I : null;
2242
+ const matchRaw = typeof opts.flags.P === "string" ? opts.flags.P : null;
2243
+ const treeOpts = {
2244
+ showHidden: opts.flags.a === true,
2245
+ maxDepth: depthRaw === null ? null : Number.parseInt(depthRaw, 10),
2246
+ ignorePattern: ignoreRaw,
2247
+ dirsOnly: opts.flags.d === true,
2248
+ matchPattern: matchRaw
2249
+ };
2250
+ const lines = [];
2251
+ for (const p of targets) {
2252
+ await walkTree(accessor, p, "", lines, treeOpts, 0);
2253
+ }
2254
+ const out = new TextEncoder().encode(lines.join("\n"));
2255
+ return [out, new IOResult20()];
2256
+ }
2257
+ var SSH_TREE = command20({
2258
+ name: "tree",
2259
+ resource: ResourceName20.SSH,
2260
+ spec: specOf20("tree"),
2261
+ fn: treeCommand
2262
+ });
2263
+
2264
+ // src/commands/builtin/ssh/wc/wc.ts
2265
+ import {
2266
+ IOResult as IOResult21,
2267
+ ResourceName as ResourceName21,
2268
+ command as command21,
2269
+ materialize as materialize5,
2270
+ resolveSource as resolveSource5,
2271
+ specOf as specOf21,
2272
+ wcAggregate
2273
+ } from "@struktoai/mirage-core";
2274
+ var ENC9 = new TextEncoder();
2275
+ var DEC2 = new TextDecoder("utf-8", { fatal: false });
2276
+ async function* wcLinesStream(source) {
2277
+ let count = 0;
2278
+ for await (const chunk of source) {
2279
+ for (let i = 0; i < chunk.byteLength; i++) if (chunk[i] === 10) count += 1;
2280
+ }
2281
+ yield ENC9.encode(String(count));
2282
+ }
2283
+ async function readStream(accessor, p) {
2284
+ return materialize5(stream(accessor, p));
2285
+ }
2286
+ async function wcCommand(accessor, paths, texts, opts) {
2287
+ const f = opts.flags;
2288
+ const lFlag = f.args_l === true;
2289
+ const wFlag = f.w === true;
2290
+ const cFlag = f.c === true;
2291
+ const mFlag = f.m === true;
2292
+ const LFlag = f.L === true;
2293
+ if (paths.length > 0) {
2294
+ const outputs = [];
2295
+ let totalLines = 0;
2296
+ let totalWords = 0;
2297
+ let totalBytes = 0;
2298
+ for (const p of paths) {
2299
+ const data = await readStream(accessor, p);
2300
+ const text2 = DEC2.decode(data);
2301
+ const lineCount = countChar(text2, "\n");
2302
+ const wordCount = text2.split(/\s+/).filter((s) => s !== "").length;
2303
+ const byteCount = data.byteLength;
2304
+ if (LFlag) {
2305
+ const maxLen = text2.split(/\r?\n/).reduce((m, l) => Math.max(m, l.length), 0);
2306
+ outputs.push(`${String(maxLen)} ${p.original}`);
2307
+ } else if (lFlag) {
2308
+ outputs.push(`${String(lineCount)} ${p.original}`);
2309
+ totalLines += lineCount;
2310
+ } else if (wFlag) {
2311
+ outputs.push(`${String(wordCount)} ${p.original}`);
2312
+ totalWords += wordCount;
2313
+ } else if (cFlag) {
2314
+ outputs.push(`${String(byteCount)} ${p.original}`);
2315
+ totalBytes += byteCount;
2316
+ } else if (mFlag) {
2317
+ const charCount = text2.length;
2318
+ outputs.push(`${String(charCount)} ${p.original}`);
2319
+ totalBytes += charCount;
2320
+ } else {
2321
+ outputs.push(
2322
+ `${String(lineCount)} ${String(wordCount)} ${String(byteCount)} ${p.original}`
2323
+ );
2324
+ totalLines += lineCount;
2325
+ totalWords += wordCount;
2326
+ totalBytes += byteCount;
2327
+ }
2328
+ }
2329
+ if (paths.length > 1) {
2330
+ if (lFlag) outputs.push(`${String(totalLines)} total`);
2331
+ else if (wFlag) outputs.push(`${String(totalWords)} total`);
2332
+ else if (cFlag) outputs.push(`${String(totalBytes)} total`);
2333
+ else if (mFlag) outputs.push(`${String(totalBytes)} total`);
2334
+ else
2335
+ outputs.push(`${String(totalLines)} ${String(totalWords)} ${String(totalBytes)} total`);
2336
+ }
2337
+ const out = ENC9.encode(outputs.join("\n"));
2338
+ return [out, new IOResult21()];
2339
+ }
2340
+ let source;
2341
+ try {
2342
+ source = resolveSource5(opts.stdin, "wc: missing operand");
2343
+ } catch (err) {
2344
+ const msg = err instanceof Error ? err.message : String(err);
2345
+ return [null, new IOResult21({ exitCode: 1, stderr: ENC9.encode(`${msg}
2346
+ `) })];
2347
+ }
2348
+ if (lFlag) return [wcLinesStream(source), new IOResult21()];
2349
+ const raw = await materialize5(source);
2350
+ const text = DEC2.decode(raw);
2351
+ const lc = countChar(text, "\n");
2352
+ const wcVal = text.split(/\s+/).filter((s) => s !== "").length;
2353
+ const bc = raw.byteLength;
2354
+ const cc = text.length;
2355
+ if (LFlag) {
2356
+ const maxLen = text.split(/\r?\n/).reduce((m, l) => Math.max(m, l.length), 0);
2357
+ return [ENC9.encode(String(maxLen)), new IOResult21()];
2358
+ }
2359
+ if (wFlag) return [ENC9.encode(String(wcVal)), new IOResult21()];
2360
+ if (mFlag) return [ENC9.encode(String(cc)), new IOResult21()];
2361
+ if (cFlag) return [ENC9.encode(String(bc)), new IOResult21()];
2362
+ return [ENC9.encode(`${String(lc)} ${String(wcVal)} ${String(bc)}`), new IOResult21()];
2363
+ }
2364
+ function countChar(text, ch) {
2365
+ let n = 0;
2366
+ for (const c of text) if (c === ch) n += 1;
2367
+ return n;
2368
+ }
2369
+ var SSH_WC = command21({
2370
+ name: "wc",
2371
+ resource: ResourceName21.SSH,
2372
+ spec: specOf21("wc"),
2373
+ fn: wcCommand,
2374
+ aggregate: wcAggregate
2375
+ });
2376
+
2377
+ // src/commands/builtin/ssh/index.ts
2378
+ var SSH_COMMANDS = [
2379
+ ...SSH_LS,
2380
+ ...SSH_TREE,
2381
+ ...SSH_CAT,
2382
+ ...SSH_HEAD,
2383
+ ...SSH_TAIL,
2384
+ ...SSH_WC,
2385
+ ...SSH_FIND,
2386
+ ...SSH_GREP,
2387
+ ...SSH_RG,
2388
+ ...SSH_STAT,
2389
+ ...SSH_JQ,
2390
+ ...SSH_DU,
2391
+ ...SSH_FILE,
2392
+ ...SSH_BASENAME,
2393
+ ...SSH_DIRNAME,
2394
+ ...SSH_REALPATH,
2395
+ ...SSH_CP,
2396
+ ...SSH_MV,
2397
+ ...SSH_RM,
2398
+ ...SSH_MKDIR,
2399
+ ...SSH_TOUCH
2400
+ ];
2401
+
2402
+ // src/core/ssh/append.ts
2403
+ async function appendBytes(accessor, p, data) {
2404
+ const sftp = await accessor.sftp();
2405
+ const virtual = stripPrefix(p);
2406
+ const remote = joinRoot(accessor.config.root ?? "/", virtual);
2407
+ await new Promise((resolveFn, rejectFn) => {
2408
+ sftp.appendFile(remote, Buffer.from(data), (err) => {
2409
+ if (err) rejectFn(err);
2410
+ else resolveFn();
2411
+ });
2412
+ });
2413
+ }
2414
+
2415
+ // src/core/ssh/create.ts
2416
+ function create(accessor, path) {
2417
+ return writeBytes(accessor, path, new Uint8Array());
2418
+ }
2419
+
2420
+ // src/core/ssh/glob.ts
2421
+ import { PathSpec as PathSpec8 } from "@struktoai/mirage-core";
2422
+ function fnmatch3(name, pattern) {
2423
+ let re = "^";
2424
+ for (const ch of pattern) {
2425
+ if (ch === "*") re += ".*";
2426
+ else if (ch === "?") re += ".";
2427
+ else if (/[.+^${}()|[\]\\]/.test(ch)) re += "\\" + ch;
2428
+ else re += ch;
2429
+ }
2430
+ re += "$";
2431
+ return new RegExp(re).test(name);
2432
+ }
2433
+ function basenameOf(p) {
2434
+ return p.slice(p.lastIndexOf("/") + 1);
2435
+ }
2436
+ async function resolveGlob(accessor, paths) {
2437
+ const result = [];
2438
+ for (const p of paths) {
2439
+ if (p.resolved) {
2440
+ result.push(p);
2441
+ } else if (p.pattern !== null && p.pattern !== "") {
2442
+ const dirSpec = new PathSpec8({
2443
+ original: p.directory,
2444
+ directory: p.directory,
2445
+ resolved: true,
2446
+ prefix: p.prefix
2447
+ });
2448
+ const entries = await readdir(accessor, dirSpec);
2449
+ const matched = [];
2450
+ for (const e of entries) {
2451
+ if (fnmatch3(basenameOf(e), p.pattern)) {
2452
+ matched.push(
2453
+ new PathSpec8({
2454
+ original: e,
2455
+ directory: p.directory,
2456
+ resolved: true,
2457
+ prefix: p.prefix
2458
+ })
2459
+ );
2460
+ }
2461
+ }
2462
+ if (matched.length > SCOPE_ERROR) matched.length = SCOPE_ERROR;
2463
+ result.push(...matched);
2464
+ } else {
2465
+ result.push(p);
2466
+ }
2467
+ }
2468
+ return result;
2469
+ }
2470
+
2471
+ // src/core/ssh/truncate.ts
2472
+ async function truncate(accessor, p, length) {
2473
+ let data;
2474
+ try {
2475
+ data = await read(accessor, p);
2476
+ } catch (err) {
2477
+ if (err.code === "ENOENT") {
2478
+ data = new Uint8Array(0);
2479
+ } else {
2480
+ throw err;
2481
+ }
2482
+ }
2483
+ const out = new Uint8Array(length);
2484
+ out.set(data.subarray(0, Math.min(data.byteLength, length)));
2485
+ await writeBytes(accessor, p, out);
2486
+ }
2487
+
2488
+ // src/ops/ssh/append.ts
2489
+ import { ResourceName as ResourceName22 } from "@struktoai/mirage-core";
2490
+ var appendOp = {
2491
+ name: "append",
2492
+ resource: ResourceName22.SSH,
2493
+ filetype: null,
2494
+ write: true,
2495
+ fn: (accessor, path, args) => {
2496
+ const data = args[0];
2497
+ if (!(data instanceof Uint8Array)) {
2498
+ throw new TypeError("append op requires a Uint8Array as the first arg");
2499
+ }
2500
+ return appendBytes(accessor, path, data);
2501
+ }
2502
+ };
2503
+
2504
+ // src/ops/ssh/create.ts
2505
+ import { ResourceName as ResourceName23 } from "@struktoai/mirage-core";
2506
+ var createOp = {
2507
+ name: "create",
2508
+ resource: ResourceName23.SSH,
2509
+ filetype: null,
2510
+ write: true,
2511
+ fn: (accessor, path) => {
2512
+ return create(accessor, path);
2513
+ }
2514
+ };
2515
+
2516
+ // src/ops/ssh/mkdir.ts
2517
+ import { ResourceName as ResourceName24 } from "@struktoai/mirage-core";
2518
+ var mkdirOp = {
2519
+ name: "mkdir",
2520
+ resource: ResourceName24.SSH,
2521
+ filetype: null,
2522
+ write: true,
2523
+ fn: (accessor, path) => {
2524
+ return mkdir(accessor, path, true);
2525
+ }
2526
+ };
2527
+
2528
+ // src/ops/ssh/read.ts
2529
+ import { ResourceName as ResourceName25 } from "@struktoai/mirage-core";
2530
+ var readOp = {
2531
+ name: "read",
2532
+ resource: ResourceName25.SSH,
2533
+ filetype: null,
2534
+ write: false,
2535
+ fn: (accessor, path) => {
2536
+ return read(accessor, path);
2537
+ }
2538
+ };
2539
+
2540
+ // src/ops/ssh/readdir.ts
2541
+ import { ResourceName as ResourceName26 } from "@struktoai/mirage-core";
2542
+ var readdirOp = {
2543
+ name: "readdir",
2544
+ resource: ResourceName26.SSH,
2545
+ filetype: null,
2546
+ write: false,
2547
+ fn: (accessor, path) => {
2548
+ return readdir(accessor, path);
2549
+ }
2550
+ };
2551
+
2552
+ // src/ops/ssh/rename.ts
2553
+ import { ResourceName as ResourceName27 } from "@struktoai/mirage-core";
2554
+ var renameOp = {
2555
+ name: "rename",
2556
+ resource: ResourceName27.SSH,
2557
+ filetype: null,
2558
+ write: true,
2559
+ fn: (accessor, path, args) => {
2560
+ const dst = args[0];
2561
+ if (dst === void 0 || dst === null || typeof dst !== "object" || !("original" in dst)) {
2562
+ throw new TypeError("rename op requires a PathSpec destination as the first arg");
2563
+ }
2564
+ return rename(accessor, path, dst);
2565
+ }
2566
+ };
2567
+
2568
+ // src/ops/ssh/rmdir.ts
2569
+ import { ResourceName as ResourceName28 } from "@struktoai/mirage-core";
2570
+ var rmdirOp = {
2571
+ name: "rmdir",
2572
+ resource: ResourceName28.SSH,
2573
+ filetype: null,
2574
+ write: true,
2575
+ fn: (accessor, path) => {
2576
+ return rmdir(accessor, path);
2577
+ }
2578
+ };
2579
+
2580
+ // src/ops/ssh/stat.ts
2581
+ import { ResourceName as ResourceName29 } from "@struktoai/mirage-core";
2582
+ var statOp = {
2583
+ name: "stat",
2584
+ resource: ResourceName29.SSH,
2585
+ filetype: null,
2586
+ write: false,
2587
+ fn: (accessor, path) => {
2588
+ return stat(accessor, path);
2589
+ }
2590
+ };
2591
+
2592
+ // src/ops/ssh/truncate.ts
2593
+ import { ResourceName as ResourceName30 } from "@struktoai/mirage-core";
2594
+ var truncateOp = {
2595
+ name: "truncate",
2596
+ resource: ResourceName30.SSH,
2597
+ filetype: null,
2598
+ write: true,
2599
+ fn: (accessor, path, args) => {
2600
+ const length = args[0];
2601
+ if (typeof length !== "number") {
2602
+ throw new TypeError("truncate op requires a number length as the first arg");
2603
+ }
2604
+ return truncate(accessor, path, length);
2605
+ }
2606
+ };
2607
+
2608
+ // src/ops/ssh/unlink.ts
2609
+ import { ResourceName as ResourceName31 } from "@struktoai/mirage-core";
2610
+ var unlinkOp = {
2611
+ name: "unlink",
2612
+ resource: ResourceName31.SSH,
2613
+ filetype: null,
2614
+ write: true,
2615
+ fn: (accessor, path) => {
2616
+ return unlink(accessor, path);
2617
+ }
2618
+ };
2619
+
2620
+ // src/ops/ssh/write.ts
2621
+ import { extractWriteData } from "@struktoai/mirage-core";
2622
+ import { ResourceName as ResourceName32 } from "@struktoai/mirage-core";
2623
+ var writeOp = {
2624
+ name: "write",
2625
+ resource: ResourceName32.SSH,
2626
+ filetype: null,
2627
+ write: true,
2628
+ fn: (accessor, path, args) => {
2629
+ const data = extractWriteData(args);
2630
+ return writeBytes(accessor, path, data);
2631
+ }
2632
+ };
2633
+
2634
+ // src/ops/ssh/index.ts
2635
+ var SSH_OPS = [
2636
+ appendOp,
2637
+ createOp,
2638
+ mkdirOp,
2639
+ readOp,
2640
+ readdirOp,
2641
+ renameOp,
2642
+ rmdirOp,
2643
+ statOp,
2644
+ truncateOp,
2645
+ unlinkOp,
2646
+ writeOp
2647
+ ];
2648
+
2649
+ // src/resource/ssh/prompt.ts
2650
+ var SSH_PROMPT = `{prefix}
2651
+ Remote filesystem via SSH/SFTP.
2652
+ IMPORTANT: This is a remote mount. Prefer targeted reads over full scans.
2653
+ Supports: ls, cat, head, tail, grep, rg, wc, find, tree, stat, mkdir, touch, cp, mv, rm, tee.`;
2654
+
2655
+ // src/resource/ssh/ssh.ts
2656
+ var SSHResource = class {
2657
+ kind = ResourceName33.SSH;
2658
+ isRemote = true;
2659
+ indexTtl = 60;
2660
+ prompt = SSH_PROMPT;
2661
+ config;
2662
+ accessor;
2663
+ index;
2664
+ opsMap = {
2665
+ read_bytes: read,
2666
+ write: writeBytes,
2667
+ readdir,
2668
+ stat,
2669
+ unlink,
2670
+ rmdir,
2671
+ copy,
2672
+ rename,
2673
+ mkdir,
2674
+ read_stream: stream,
2675
+ range_read: rangeRead,
2676
+ rm_recursive: rmR,
2677
+ du_total: du,
2678
+ du_all: duAll,
2679
+ create,
2680
+ truncate,
2681
+ exists: exists3,
2682
+ find_flat: find,
2683
+ append: appendBytes
2684
+ };
2685
+ constructor(config) {
2686
+ this.config = config;
2687
+ this.accessor = new SSHAccessor(config);
2688
+ this.index = new RAMIndexCacheStore({ ttl: this.indexTtl });
2689
+ }
2690
+ open() {
2691
+ return Promise.resolve();
2692
+ }
2693
+ close() {
2694
+ return this.accessor.close();
2695
+ }
2696
+ ops() {
2697
+ return SSH_OPS;
2698
+ }
2699
+ commands() {
2700
+ return SSH_COMMANDS;
2701
+ }
2702
+ streamPath(p) {
2703
+ return stream(this.accessor, p);
2704
+ }
2705
+ readFile(p) {
2706
+ return read(this.accessor, p);
2707
+ }
2708
+ writeFile(p, data) {
2709
+ return writeBytes(this.accessor, p, data);
2710
+ }
2711
+ appendFile(p, data) {
2712
+ return appendBytes(this.accessor, p, data);
2713
+ }
2714
+ readdir(p) {
2715
+ return readdir(this.accessor, p);
2716
+ }
2717
+ stat(p) {
2718
+ return stat(this.accessor, p);
2719
+ }
2720
+ exists(p) {
2721
+ return exists3(this.accessor, p);
2722
+ }
2723
+ mkdir(p, options) {
2724
+ return mkdir(this.accessor, p, options?.recursive === true);
2725
+ }
2726
+ rmdir(p) {
2727
+ return rmdir(this.accessor, p);
2728
+ }
2729
+ unlink(p) {
2730
+ return unlink(this.accessor, p);
2731
+ }
2732
+ rename(src, dst) {
2733
+ return rename(this.accessor, src, dst);
2734
+ }
2735
+ truncate(p, length) {
2736
+ return truncate(this.accessor, p, length);
2737
+ }
2738
+ copy(src, dst) {
2739
+ return copy(this.accessor, src, dst);
2740
+ }
2741
+ rmR(p) {
2742
+ return rmR(this.accessor, p);
2743
+ }
2744
+ du(p) {
2745
+ return du(this.accessor, p);
2746
+ }
2747
+ find(p, options = {}) {
2748
+ return find(this.accessor, p, options);
2749
+ }
2750
+ async fingerprint(p) {
2751
+ try {
2752
+ const remote = await stat(this.accessor, p);
2753
+ const mtime = remote.modified ?? "";
2754
+ const size = String(remote.size ?? 0);
2755
+ return `${mtime}:${size}`;
2756
+ } catch (err) {
2757
+ if (err.code === "ENOENT") return null;
2758
+ throw err;
2759
+ }
2760
+ }
2761
+ glob(paths, prefix = "") {
2762
+ const effective = prefix ? paths.map(
2763
+ (p) => p.prefix ? p : new PathSpec9({
2764
+ original: p.original,
2765
+ directory: p.directory,
2766
+ ...p.pattern !== null ? { pattern: p.pattern } : {},
2767
+ resolved: p.resolved,
2768
+ prefix
2769
+ })
2770
+ ) : paths;
2771
+ return resolveGlob(this.accessor, effective);
2772
+ }
2773
+ // eslint-disable-next-line @typescript-eslint/require-await
2774
+ async getState() {
2775
+ return {
2776
+ type: this.kind,
2777
+ needsOverride: false,
2778
+ redactedFields: ["password", "passphrase"],
2779
+ config: redactSshConfig(this.config)
2780
+ };
2781
+ }
2782
+ loadState(_state) {
2783
+ return Promise.resolve();
2784
+ }
2785
+ };
2786
+
2787
+ export {
2788
+ SSHAccessor,
2789
+ SSH_COMMANDS,
2790
+ SSH_OPS,
2791
+ SSH_PROMPT,
2792
+ SSHResource
2793
+ };
2794
+ //# sourceMappingURL=chunk-M42JTWPR.js.map