@aigne/afs-cli 1.11.0-beta.1 → 1.11.0-beta.11

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 (348) hide show
  1. package/README.md +262 -15
  2. package/dist/_virtual/rolldown_runtime.cjs +29 -0
  3. package/dist/_virtual/rolldown_runtime.mjs +7 -0
  4. package/dist/cli.cjs +40 -0
  5. package/dist/cli.d.cts +2 -0
  6. package/dist/cli.d.mts +2 -1
  7. package/dist/cli.mjs +36 -24
  8. package/dist/cli.mjs.map +1 -0
  9. package/dist/config/afs-loader.cjs +578 -0
  10. package/dist/config/afs-loader.d.cts +19 -0
  11. package/dist/config/afs-loader.d.cts.map +1 -0
  12. package/dist/config/afs-loader.d.mts +19 -0
  13. package/dist/config/afs-loader.d.mts.map +1 -0
  14. package/dist/config/afs-loader.mjs +576 -0
  15. package/dist/config/afs-loader.mjs.map +1 -0
  16. package/dist/config/env.cjs +46 -0
  17. package/dist/config/env.mjs +46 -0
  18. package/dist/config/env.mjs.map +1 -0
  19. package/dist/config/loader.cjs +219 -0
  20. package/dist/config/loader.mjs +217 -0
  21. package/dist/config/loader.mjs.map +1 -0
  22. package/dist/config/mount-commands.cjs +226 -0
  23. package/dist/config/mount-commands.d.cts +14 -0
  24. package/dist/config/mount-commands.d.cts.map +1 -0
  25. package/dist/config/mount-commands.d.mts +14 -0
  26. package/dist/config/mount-commands.d.mts.map +1 -0
  27. package/dist/config/mount-commands.mjs +220 -0
  28. package/dist/config/mount-commands.mjs.map +1 -0
  29. package/dist/config/schema.cjs +99 -0
  30. package/dist/config/schema.mjs +98 -0
  31. package/dist/config/schema.mjs.map +1 -0
  32. package/dist/core/commands/delete.cjs +41 -0
  33. package/dist/core/commands/delete.d.cts +18 -0
  34. package/dist/core/commands/delete.d.cts.map +1 -0
  35. package/dist/core/commands/delete.d.mts +18 -0
  36. package/dist/core/commands/delete.d.mts.map +1 -0
  37. package/dist/core/commands/delete.mjs +42 -0
  38. package/dist/core/commands/delete.mjs.map +1 -0
  39. package/dist/core/commands/exec.cjs +98 -0
  40. package/dist/core/commands/exec.d.cts +26 -0
  41. package/dist/core/commands/exec.d.cts.map +1 -0
  42. package/dist/core/commands/exec.d.mts +26 -0
  43. package/dist/core/commands/exec.d.mts.map +1 -0
  44. package/dist/core/commands/exec.mjs +99 -0
  45. package/dist/core/commands/exec.mjs.map +1 -0
  46. package/dist/core/commands/explain.cjs +278 -0
  47. package/dist/core/commands/explain.d.cts +25 -0
  48. package/dist/core/commands/explain.d.cts.map +1 -0
  49. package/dist/core/commands/explain.d.mts +25 -0
  50. package/dist/core/commands/explain.d.mts.map +1 -0
  51. package/dist/core/commands/explain.mjs +279 -0
  52. package/dist/core/commands/explain.mjs.map +1 -0
  53. package/dist/core/commands/explore.cjs +30 -0
  54. package/dist/core/commands/explore.d.mts +2 -0
  55. package/dist/core/commands/explore.mjs +31 -0
  56. package/dist/core/commands/explore.mjs.map +1 -0
  57. package/dist/core/commands/index.cjs +36 -0
  58. package/dist/core/commands/index.d.cts +21 -0
  59. package/dist/core/commands/index.d.cts.map +1 -0
  60. package/dist/core/commands/index.d.mts +24 -0
  61. package/dist/core/commands/index.d.mts.map +1 -0
  62. package/dist/core/commands/index.mjs +37 -0
  63. package/dist/core/commands/index.mjs.map +1 -0
  64. package/dist/core/commands/ls.cjs +57 -0
  65. package/dist/core/commands/ls.d.cts +21 -0
  66. package/dist/core/commands/ls.d.cts.map +1 -0
  67. package/dist/core/commands/ls.d.mts +21 -0
  68. package/dist/core/commands/ls.d.mts.map +1 -0
  69. package/dist/core/commands/ls.mjs +58 -0
  70. package/dist/core/commands/ls.mjs.map +1 -0
  71. package/dist/core/commands/mount.cjs +222 -0
  72. package/dist/core/commands/mount.d.cts +35 -0
  73. package/dist/core/commands/mount.d.cts.map +1 -0
  74. package/dist/core/commands/mount.d.mts +35 -0
  75. package/dist/core/commands/mount.d.mts.map +1 -0
  76. package/dist/core/commands/mount.mjs +223 -0
  77. package/dist/core/commands/mount.mjs.map +1 -0
  78. package/dist/core/commands/read.cjs +48 -0
  79. package/dist/core/commands/read.d.cts +17 -0
  80. package/dist/core/commands/read.d.cts.map +1 -0
  81. package/dist/core/commands/read.d.mts +17 -0
  82. package/dist/core/commands/read.d.mts.map +1 -0
  83. package/dist/core/commands/read.mjs +49 -0
  84. package/dist/core/commands/read.mjs.map +1 -0
  85. package/dist/core/commands/search.cjs +40 -0
  86. package/dist/core/commands/search.d.mts +2 -0
  87. package/dist/core/commands/search.mjs +41 -0
  88. package/dist/core/commands/search.mjs.map +1 -0
  89. package/dist/core/commands/serve.cjs +267 -0
  90. package/dist/core/commands/serve.d.mts +2 -0
  91. package/dist/core/commands/serve.mjs +267 -0
  92. package/dist/core/commands/serve.mjs.map +1 -0
  93. package/dist/core/commands/stat.cjs +53 -0
  94. package/dist/core/commands/stat.d.cts +17 -0
  95. package/dist/core/commands/stat.d.cts.map +1 -0
  96. package/dist/core/commands/stat.d.mts +17 -0
  97. package/dist/core/commands/stat.d.mts.map +1 -0
  98. package/dist/core/commands/stat.mjs +54 -0
  99. package/dist/core/commands/stat.mjs.map +1 -0
  100. package/dist/core/commands/types.cjs +18 -0
  101. package/dist/core/commands/types.d.cts +54 -0
  102. package/dist/core/commands/types.d.cts.map +1 -0
  103. package/dist/core/commands/types.d.mts +54 -0
  104. package/dist/core/commands/types.d.mts.map +1 -0
  105. package/dist/core/commands/types.mjs +19 -0
  106. package/dist/core/commands/types.mjs.map +1 -0
  107. package/dist/core/commands/write.cjs +70 -0
  108. package/dist/core/commands/write.d.cts +20 -0
  109. package/dist/core/commands/write.d.cts.map +1 -0
  110. package/dist/core/commands/write.d.mts +20 -0
  111. package/dist/core/commands/write.d.mts.map +1 -0
  112. package/dist/core/commands/write.mjs +71 -0
  113. package/dist/core/commands/write.mjs.map +1 -0
  114. package/dist/core/executor/index.cjs +196 -0
  115. package/dist/core/executor/index.d.cts +77 -0
  116. package/dist/core/executor/index.d.cts.map +1 -0
  117. package/dist/core/executor/index.d.mts +77 -0
  118. package/dist/core/executor/index.d.mts.map +1 -0
  119. package/dist/core/executor/index.mjs +195 -0
  120. package/dist/core/executor/index.mjs.map +1 -0
  121. package/dist/core/formatters/delete.cjs +37 -0
  122. package/dist/core/formatters/delete.d.cts +18 -0
  123. package/dist/core/formatters/delete.d.cts.map +1 -0
  124. package/dist/core/formatters/delete.d.mts +18 -0
  125. package/dist/core/formatters/delete.d.mts.map +1 -0
  126. package/dist/core/formatters/delete.mjs +37 -0
  127. package/dist/core/formatters/delete.mjs.map +1 -0
  128. package/dist/core/formatters/exec.cjs +60 -0
  129. package/dist/core/formatters/exec.d.cts +18 -0
  130. package/dist/core/formatters/exec.d.cts.map +1 -0
  131. package/dist/core/formatters/exec.d.mts +18 -0
  132. package/dist/core/formatters/exec.d.mts.map +1 -0
  133. package/dist/core/formatters/exec.mjs +60 -0
  134. package/dist/core/formatters/exec.mjs.map +1 -0
  135. package/dist/core/formatters/explain.cjs +99 -0
  136. package/dist/core/formatters/explain.d.cts +11 -0
  137. package/dist/core/formatters/explain.d.cts.map +1 -0
  138. package/dist/core/formatters/explain.d.mts +11 -0
  139. package/dist/core/formatters/explain.d.mts.map +1 -0
  140. package/dist/core/formatters/explain.mjs +98 -0
  141. package/dist/core/formatters/explain.mjs.map +1 -0
  142. package/dist/core/formatters/index.d.mts +9 -0
  143. package/dist/core/formatters/ls.cjs +179 -0
  144. package/dist/core/formatters/ls.d.cts +20 -0
  145. package/dist/core/formatters/ls.d.cts.map +1 -0
  146. package/dist/core/formatters/ls.d.mts +20 -0
  147. package/dist/core/formatters/ls.d.mts.map +1 -0
  148. package/dist/core/formatters/ls.mjs +179 -0
  149. package/dist/core/formatters/ls.mjs.map +1 -0
  150. package/dist/core/formatters/mount.cjs +55 -0
  151. package/dist/core/formatters/mount.d.cts +15 -0
  152. package/dist/core/formatters/mount.d.cts.map +1 -0
  153. package/dist/core/formatters/mount.d.mts +15 -0
  154. package/dist/core/formatters/mount.d.mts.map +1 -0
  155. package/dist/core/formatters/mount.mjs +55 -0
  156. package/dist/core/formatters/mount.mjs.map +1 -0
  157. package/dist/core/formatters/read.cjs +100 -0
  158. package/dist/core/formatters/read.d.cts +22 -0
  159. package/dist/core/formatters/read.d.cts.map +1 -0
  160. package/dist/core/formatters/read.d.mts +22 -0
  161. package/dist/core/formatters/read.d.mts.map +1 -0
  162. package/dist/core/formatters/read.mjs +100 -0
  163. package/dist/core/formatters/read.mjs.map +1 -0
  164. package/dist/core/formatters/search.cjs +44 -0
  165. package/dist/core/formatters/search.d.mts +1 -0
  166. package/dist/core/formatters/search.mjs +44 -0
  167. package/dist/core/formatters/search.mjs.map +1 -0
  168. package/dist/core/formatters/stat.cjs +155 -0
  169. package/dist/core/formatters/stat.d.cts +15 -0
  170. package/dist/core/formatters/stat.d.cts.map +1 -0
  171. package/dist/core/formatters/stat.d.mts +15 -0
  172. package/dist/core/formatters/stat.d.mts.map +1 -0
  173. package/dist/core/formatters/stat.mjs +155 -0
  174. package/dist/core/formatters/stat.mjs.map +1 -0
  175. package/dist/core/formatters/write.cjs +51 -0
  176. package/dist/core/formatters/write.d.cts +22 -0
  177. package/dist/core/formatters/write.d.cts.map +1 -0
  178. package/dist/core/formatters/write.d.mts +22 -0
  179. package/dist/core/formatters/write.d.mts.map +1 -0
  180. package/dist/core/formatters/write.mjs +51 -0
  181. package/dist/core/formatters/write.mjs.map +1 -0
  182. package/dist/core/helpers/exec-args.cjs +142 -0
  183. package/dist/core/helpers/exec-args.d.cts +46 -0
  184. package/dist/core/helpers/exec-args.d.cts.map +1 -0
  185. package/dist/core/helpers/exec-args.d.mts +46 -0
  186. package/dist/core/helpers/exec-args.d.mts.map +1 -0
  187. package/dist/core/helpers/exec-args.mjs +139 -0
  188. package/dist/core/helpers/exec-args.mjs.map +1 -0
  189. package/dist/core/helpers/stdin.cjs +41 -0
  190. package/dist/core/helpers/stdin.d.cts +15 -0
  191. package/dist/core/helpers/stdin.d.cts.map +1 -0
  192. package/dist/core/helpers/stdin.d.mts +15 -0
  193. package/dist/core/helpers/stdin.d.mts.map +1 -0
  194. package/dist/core/helpers/stdin.mjs +41 -0
  195. package/dist/core/helpers/stdin.mjs.map +1 -0
  196. package/dist/core/index.cjs +49 -0
  197. package/dist/core/index.d.cts +24 -0
  198. package/dist/core/index.d.mts +25 -0
  199. package/dist/core/index.mjs +24 -0
  200. package/dist/core/path-utils.cjs +1 -0
  201. package/dist/core/path-utils.mjs +3 -0
  202. package/dist/core/types.d.cts +24 -0
  203. package/dist/core/types.d.cts.map +1 -0
  204. package/dist/core/types.d.mts +24 -0
  205. package/dist/core/types.d.mts.map +1 -0
  206. package/dist/credential/auth-server.cjs +247 -0
  207. package/dist/credential/auth-server.mjs +247 -0
  208. package/dist/credential/auth-server.mjs.map +1 -0
  209. package/dist/credential/cli-auth-context.cjs +86 -0
  210. package/dist/credential/cli-auth-context.d.mts +1 -0
  211. package/dist/credential/cli-auth-context.mjs +86 -0
  212. package/dist/credential/cli-auth-context.mjs.map +1 -0
  213. package/dist/credential/index.cjs +5 -0
  214. package/dist/credential/index.d.mts +4 -0
  215. package/dist/credential/index.mjs +7 -0
  216. package/dist/credential/mcp-auth-context.cjs +192 -0
  217. package/dist/credential/mcp-auth-context.d.mts +1 -0
  218. package/dist/credential/mcp-auth-context.mjs +192 -0
  219. package/dist/credential/mcp-auth-context.mjs.map +1 -0
  220. package/dist/credential/resolver.cjs +127 -0
  221. package/dist/credential/resolver.d.mts +1 -0
  222. package/dist/credential/resolver.mjs +127 -0
  223. package/dist/credential/resolver.mjs.map +1 -0
  224. package/dist/credential/store.cjs +106 -0
  225. package/dist/credential/store.d.cts +30 -0
  226. package/dist/credential/store.d.cts.map +1 -0
  227. package/dist/credential/store.d.mts +30 -0
  228. package/dist/credential/store.d.mts.map +1 -0
  229. package/dist/credential/store.mjs +106 -0
  230. package/dist/credential/store.mjs.map +1 -0
  231. package/dist/errors.cjs +18 -0
  232. package/dist/errors.mjs +18 -0
  233. package/dist/errors.mjs.map +1 -0
  234. package/dist/explorer/actions.cjs +311 -0
  235. package/dist/explorer/actions.mjs +305 -0
  236. package/dist/explorer/actions.mjs.map +1 -0
  237. package/dist/explorer/components/dialog.cjs +508 -0
  238. package/dist/explorer/components/dialog.mjs +509 -0
  239. package/dist/explorer/components/dialog.mjs.map +1 -0
  240. package/dist/explorer/components/file-list.cjs +107 -0
  241. package/dist/explorer/components/file-list.mjs +107 -0
  242. package/dist/explorer/components/file-list.mjs.map +1 -0
  243. package/dist/explorer/components/function-bar.cjs +55 -0
  244. package/dist/explorer/components/function-bar.mjs +55 -0
  245. package/dist/explorer/components/function-bar.mjs.map +1 -0
  246. package/dist/explorer/components/index.cjs +5 -0
  247. package/dist/explorer/components/index.mjs +7 -0
  248. package/dist/explorer/components/metadata-panel.cjs +219 -0
  249. package/dist/explorer/components/metadata-panel.mjs +219 -0
  250. package/dist/explorer/components/metadata-panel.mjs.map +1 -0
  251. package/dist/explorer/components/status-bar.cjs +53 -0
  252. package/dist/explorer/components/status-bar.mjs +54 -0
  253. package/dist/explorer/components/status-bar.mjs.map +1 -0
  254. package/dist/explorer/keybindings.cjs +214 -0
  255. package/dist/explorer/keybindings.mjs +213 -0
  256. package/dist/explorer/keybindings.mjs.map +1 -0
  257. package/dist/explorer/screen.cjs +251 -0
  258. package/dist/explorer/screen.d.cts +23 -0
  259. package/dist/explorer/screen.d.cts.map +1 -0
  260. package/dist/explorer/screen.d.mts +23 -0
  261. package/dist/explorer/screen.d.mts.map +1 -0
  262. package/dist/explorer/screen.mjs +250 -0
  263. package/dist/explorer/screen.mjs.map +1 -0
  264. package/dist/explorer/state.cjs +53 -0
  265. package/dist/explorer/state.mjs +53 -0
  266. package/dist/explorer/state.mjs.map +1 -0
  267. package/dist/explorer/theme.cjs +160 -0
  268. package/dist/explorer/theme.mjs +157 -0
  269. package/dist/explorer/theme.mjs.map +1 -0
  270. package/dist/index.cjs +12 -0
  271. package/dist/index.d.cts +6 -0
  272. package/dist/index.d.mts +7 -4
  273. package/dist/index.mjs +7 -2
  274. package/dist/mcp/http-transport.cjs +87 -0
  275. package/dist/mcp/http-transport.mjs +87 -0
  276. package/dist/mcp/http-transport.mjs.map +1 -0
  277. package/dist/mcp/prompts.cjs +48 -0
  278. package/dist/mcp/prompts.mjs +48 -0
  279. package/dist/mcp/prompts.mjs.map +1 -0
  280. package/dist/mcp/resources.cjs +25 -0
  281. package/dist/mcp/resources.mjs +25 -0
  282. package/dist/mcp/resources.mjs.map +1 -0
  283. package/dist/mcp/server.cjs +74 -0
  284. package/dist/mcp/server.mjs +73 -0
  285. package/dist/mcp/server.mjs.map +1 -0
  286. package/dist/mcp/tools.cjs +152 -0
  287. package/dist/mcp/tools.mjs +152 -0
  288. package/dist/mcp/tools.mjs.map +1 -0
  289. package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/index.d.cts +10 -0
  290. package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/index.d.cts.map +1 -0
  291. package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/index.d.mts +10 -0
  292. package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/index.d.mts.map +1 -0
  293. package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/types.d.cts +46 -0
  294. package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/types.d.cts.map +1 -0
  295. package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/types.d.mts +46 -0
  296. package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/types.d.mts.map +1 -0
  297. package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/urlpattern.cjs +902 -0
  298. package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/urlpattern.mjs +902 -0
  299. package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/urlpattern.mjs.map +1 -0
  300. package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/index.cjs +6 -0
  301. package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/index.mjs +8 -0
  302. package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/index.mjs.map +1 -0
  303. package/dist/path-utils.cjs +105 -0
  304. package/dist/path-utils.d.cts +50 -0
  305. package/dist/path-utils.d.cts.map +1 -0
  306. package/dist/path-utils.d.mts +50 -0
  307. package/dist/path-utils.d.mts.map +1 -0
  308. package/dist/path-utils.mjs +104 -0
  309. package/dist/path-utils.mjs.map +1 -0
  310. package/dist/repl.cjs +491 -0
  311. package/dist/repl.d.cts +15 -0
  312. package/dist/repl.d.cts.map +1 -0
  313. package/dist/repl.d.mts +16 -0
  314. package/dist/repl.d.mts.map +1 -0
  315. package/dist/repl.mjs +491 -0
  316. package/dist/repl.mjs.map +1 -0
  317. package/dist/serve.cjs +146 -0
  318. package/dist/serve.d.cts +41 -0
  319. package/dist/serve.d.cts.map +1 -0
  320. package/dist/serve.d.mts +41 -0
  321. package/dist/serve.d.mts.map +1 -0
  322. package/dist/serve.mjs +146 -0
  323. package/dist/serve.mjs.map +1 -0
  324. package/dist/ui/header.cjs +12 -0
  325. package/dist/ui/header.mjs +13 -0
  326. package/dist/ui/header.mjs.map +1 -0
  327. package/dist/ui/index.cjs +8 -0
  328. package/dist/ui/index.mjs +9 -0
  329. package/dist/ui/index.mjs.map +1 -0
  330. package/dist/ui/terminal.cjs +88 -0
  331. package/dist/ui/terminal.mjs +88 -0
  332. package/dist/ui/terminal.mjs.map +1 -0
  333. package/dist/version.cjs +9 -0
  334. package/dist/version.d.cts +5 -0
  335. package/dist/version.d.cts.map +1 -0
  336. package/dist/version.d.mts +5 -0
  337. package/dist/version.d.mts.map +1 -0
  338. package/dist/version.mjs +9 -0
  339. package/dist/version.mjs.map +1 -0
  340. package/package.json +77 -11
  341. package/.turbo/turbo-build.log +0 -18
  342. package/.turbo/turbo-check-types.log +0 -4
  343. package/dist/version--p6A8sKX.mjs +0 -5
  344. package/src/cli.test.ts +0 -8
  345. package/src/cli.ts +0 -29
  346. package/src/index.ts +0 -7
  347. package/src/version.ts +0 -1
  348. package/tsconfig.json +0 -16
@@ -0,0 +1,311 @@
1
+
2
+ //#region src/explorer/actions.ts
3
+ /**
4
+ * Check if an entry is executable based on its meta
5
+ */
6
+ function isExecutable(meta) {
7
+ if (!meta) return false;
8
+ if (Array.isArray(meta.kinds)) return meta.kinds.includes("afs:executable");
9
+ return meta.kind === "afs:executable";
10
+ }
11
+ /**
12
+ * Check if a value is a valid directory-indicating childrenCount
13
+ *
14
+ * Per Provider Protocol spec:
15
+ * - childrenCount > 0: known children → directory
16
+ * - childrenCount = -1: unknown children count → directory
17
+ * - childrenCount = 0 or undefined: leaf node → file
18
+ */
19
+ function isDirectory(childrenCount) {
20
+ if (typeof childrenCount !== "number" || Number.isNaN(childrenCount)) return false;
21
+ return childrenCount === -1 || childrenCount > 0;
22
+ }
23
+ /**
24
+ * Convert AFS entry to explorer entry
25
+ *
26
+ * Type determination priority:
27
+ * 1. afs:executable → exec
28
+ * 2. afs:link → link
29
+ * 3. childrenCount > 0 or -1 → directory
30
+ * 4. Otherwise (childrenCount = 0, undefined, invalid) → file
31
+ */
32
+ function toExplorerEntry(entry, _basePath) {
33
+ const name = entry.path.split("/").pop() || entry.path;
34
+ const metadata = entry.meta || {};
35
+ let type = "file";
36
+ if (isExecutable(metadata)) type = "exec";
37
+ else if (metadata.kind === "afs:link" || metadata.kinds?.includes("afs:link")) type = "link";
38
+ else if (isDirectory(metadata.childrenCount)) type = "directory";
39
+ return {
40
+ name,
41
+ path: entry.path,
42
+ type,
43
+ size: metadata.size,
44
+ modified: entry.updatedAt instanceof Date ? entry.updatedAt : void 0,
45
+ childrenCount: metadata.childrenCount,
46
+ hash: metadata.hash,
47
+ description: metadata.description,
48
+ provider: metadata.provider,
49
+ icon: metadata.icon,
50
+ kind: metadata.kind,
51
+ kinds: Array.isArray(metadata.kinds) ? metadata.kinds : void 0,
52
+ label: metadata.label,
53
+ tags: Array.isArray(metadata.tags) ? metadata.tags : void 0,
54
+ actions: entry.actions
55
+ };
56
+ }
57
+ /**
58
+ * Create parent directory entry
59
+ */
60
+ function createUpEntry(parentPath) {
61
+ return {
62
+ name: "..",
63
+ path: parentPath,
64
+ type: "up"
65
+ };
66
+ }
67
+ /**
68
+ * Build immediate children from a list of deep paths
69
+ * For example, if path is "/" and entries contain "/github/ArcBlock/afs",
70
+ * this returns a virtual directory entry for "/github"
71
+ */
72
+ function buildImmediateChildren(path, afsEntries) {
73
+ const normalizedPath = path === "/" ? "" : path;
74
+ const pathDepth = normalizedPath === "" ? 0 : normalizedPath.split("/").filter(Boolean).length;
75
+ const childrenMap = /* @__PURE__ */ new Map();
76
+ for (const entry of afsEntries) {
77
+ if (entry.path === path) continue;
78
+ const entryParts = entry.path.split("/").filter(Boolean);
79
+ const childName = entryParts[pathDepth];
80
+ if (!childName) continue;
81
+ const childPath = `/${entryParts.slice(0, pathDepth + 1).join("/")}`;
82
+ if (entryParts.length === pathDepth + 1) childrenMap.set(childName, toExplorerEntry(entry, path));
83
+ else if (!childrenMap.has(childName)) childrenMap.set(childName, {
84
+ name: childName,
85
+ path: childPath,
86
+ type: "directory",
87
+ childrenCount: -1
88
+ });
89
+ }
90
+ return childrenMap;
91
+ }
92
+ /**
93
+ * Load directory entries from AFS
94
+ */
95
+ async function loadDirectory(afs, path) {
96
+ try {
97
+ const result = await afs.list(path, { maxDepth: 1 });
98
+ const entries = [];
99
+ if (path !== "/") {
100
+ const parentPath = path.split("/").slice(0, -1).join("/") || "/";
101
+ entries.push(createUpEntry(parentPath));
102
+ }
103
+ const childrenMap = buildImmediateChildren(path, result.data);
104
+ entries.push(...childrenMap.values());
105
+ entries.sort((a, b) => {
106
+ if (a.type === "up") return -1;
107
+ if (b.type === "up") return 1;
108
+ const aIsDir = a.type === "directory";
109
+ const bIsDir = b.type === "directory";
110
+ if (aIsDir && !bIsDir) return -1;
111
+ if (!aIsDir && bIsDir) return 1;
112
+ return a.name.localeCompare(b.name);
113
+ });
114
+ return { entries };
115
+ } catch (error) {
116
+ return {
117
+ entries: [],
118
+ error: error instanceof Error ? error.message : "Failed to load directory"
119
+ };
120
+ }
121
+ }
122
+ /**
123
+ * Load metadata for an entry using stat() to get enriched data (including actions)
124
+ */
125
+ async function loadMetadata(afs, entry) {
126
+ if (entry.type === "up") return;
127
+ try {
128
+ const data = (await afs.stat(entry.path)).data;
129
+ if (!data) return {
130
+ path: entry.path,
131
+ size: entry.size,
132
+ modified: entry.modified
133
+ };
134
+ const meta = data.meta || {};
135
+ return {
136
+ path: entry.path,
137
+ size: meta.size,
138
+ modified: data.updatedAt instanceof Date ? data.updatedAt : void 0,
139
+ childrenCount: meta.childrenCount,
140
+ hash: meta.hash,
141
+ description: meta.description,
142
+ provider: meta.provider,
143
+ mountPath: meta.mountPath,
144
+ uri: meta.uri,
145
+ permissions: meta.permissions,
146
+ actions: data.actions,
147
+ extra: meta
148
+ };
149
+ } catch {
150
+ return {
151
+ path: entry.path,
152
+ size: entry.size,
153
+ modified: entry.modified
154
+ };
155
+ }
156
+ }
157
+ /**
158
+ * Get explain output for an entry
159
+ */
160
+ async function getExplain(afs, path) {
161
+ try {
162
+ const entry = (await afs.list(path, { maxDepth: 0 })).data[0];
163
+ if (!entry) return {
164
+ content: "",
165
+ error: "Entry not found"
166
+ };
167
+ const metadata = entry.meta || {};
168
+ const lines = [];
169
+ lines.push(`OBJECT ${path}`);
170
+ lines.push("");
171
+ if (metadata.description) {
172
+ lines.push("DESCRIPTION");
173
+ lines.push(metadata.description);
174
+ lines.push("");
175
+ }
176
+ if (metadata.size !== void 0) {
177
+ lines.push("SIZE");
178
+ lines.push(`${metadata.size} bytes`);
179
+ lines.push("");
180
+ }
181
+ if (metadata.childrenCount !== void 0) {
182
+ lines.push("CHILDREN");
183
+ lines.push(`${metadata.childrenCount} items`);
184
+ lines.push("");
185
+ }
186
+ if (metadata.provider) {
187
+ lines.push("PROVIDER");
188
+ lines.push(metadata.provider);
189
+ lines.push("");
190
+ }
191
+ if (metadata.hash) {
192
+ lines.push("HASH");
193
+ lines.push(metadata.hash);
194
+ lines.push("");
195
+ }
196
+ return { content: lines.join("\n") };
197
+ } catch (error) {
198
+ return {
199
+ content: "",
200
+ error: error instanceof Error ? error.message : "Failed to get explain"
201
+ };
202
+ }
203
+ }
204
+ /**
205
+ * Execute an action on an entry
206
+ *
207
+ * For executable entries (kinds includes "afs:executable"), this calls
208
+ * the underlying exec command. The action parameter is currently not used
209
+ * but reserved for future action selection.
210
+ */
211
+ async function executeAction(afs, path, _action, params) {
212
+ try {
213
+ const result = await afs.exec(path, params || {}, {});
214
+ return {
215
+ success: result.success,
216
+ message: result.error?.message,
217
+ data: result.data
218
+ };
219
+ } catch (error) {
220
+ return {
221
+ success: false,
222
+ message: error instanceof Error ? error.message : String(error)
223
+ };
224
+ }
225
+ }
226
+ /**
227
+ * Read file content
228
+ */
229
+ async function readFileContent(afs, path) {
230
+ try {
231
+ const entry = (await afs.read(path)).data;
232
+ if (!entry) return {
233
+ content: "",
234
+ error: "File not found"
235
+ };
236
+ const rawContent = entry.content;
237
+ if (rawContent === void 0 || rawContent === null) return {
238
+ content: "",
239
+ error: "No content available"
240
+ };
241
+ let content;
242
+ if (typeof rawContent === "string") content = rawContent;
243
+ else if (Buffer.isBuffer(rawContent)) content = rawContent.toString("utf-8");
244
+ else if (rawContent instanceof Uint8Array) content = Buffer.from(rawContent).toString("utf-8");
245
+ else if (rawContent instanceof ArrayBuffer) content = Buffer.from(new Uint8Array(rawContent)).toString("utf-8");
246
+ else if (typeof rawContent === "object") content = JSON.stringify(rawContent, null, 2);
247
+ else content = String(rawContent);
248
+ return { content };
249
+ } catch (error) {
250
+ return {
251
+ content: "",
252
+ error: error instanceof Error ? error.message : "Failed to read file"
253
+ };
254
+ }
255
+ }
256
+ /**
257
+ * Navigation helpers
258
+ */
259
+ const navigation = {
260
+ up(state) {
261
+ return { selectedIndex: Math.max(0, state.selectedIndex - 1) };
262
+ },
263
+ down(state) {
264
+ return { selectedIndex: Math.min(state.entries.length - 1, state.selectedIndex + 1) };
265
+ },
266
+ home(_state) {
267
+ return {
268
+ selectedIndex: 0,
269
+ scrollOffset: 0
270
+ };
271
+ },
272
+ end(state) {
273
+ return { selectedIndex: Math.max(0, state.entries.length - 1) };
274
+ },
275
+ pageUp(state, pageSize) {
276
+ return { selectedIndex: Math.max(0, state.selectedIndex - pageSize) };
277
+ },
278
+ pageDown(state, pageSize) {
279
+ return { selectedIndex: Math.min(state.entries.length - 1, state.selectedIndex + pageSize) };
280
+ },
281
+ getSelected(state) {
282
+ return state.entries[state.selectedIndex];
283
+ },
284
+ getParentPath(path) {
285
+ if (path === "/") return "/";
286
+ const parts = path.split("/").filter(Boolean);
287
+ parts.pop();
288
+ return `/${parts.join("/")}` || "/";
289
+ }
290
+ };
291
+ /**
292
+ * Create initial state
293
+ */
294
+ function createInitialState(startPath = "/") {
295
+ return {
296
+ currentPath: startPath,
297
+ entries: [],
298
+ selectedIndex: 0,
299
+ scrollOffset: 0,
300
+ loading: true
301
+ };
302
+ }
303
+
304
+ //#endregion
305
+ exports.createInitialState = createInitialState;
306
+ exports.executeAction = executeAction;
307
+ exports.getExplain = getExplain;
308
+ exports.loadDirectory = loadDirectory;
309
+ exports.loadMetadata = loadMetadata;
310
+ exports.navigation = navigation;
311
+ exports.readFileContent = readFileContent;
@@ -0,0 +1,305 @@
1
+ //#region src/explorer/actions.ts
2
+ /**
3
+ * Check if an entry is executable based on its meta
4
+ */
5
+ function isExecutable(meta) {
6
+ if (!meta) return false;
7
+ if (Array.isArray(meta.kinds)) return meta.kinds.includes("afs:executable");
8
+ return meta.kind === "afs:executable";
9
+ }
10
+ /**
11
+ * Check if a value is a valid directory-indicating childrenCount
12
+ *
13
+ * Per Provider Protocol spec:
14
+ * - childrenCount > 0: known children → directory
15
+ * - childrenCount = -1: unknown children count → directory
16
+ * - childrenCount = 0 or undefined: leaf node → file
17
+ */
18
+ function isDirectory(childrenCount) {
19
+ if (typeof childrenCount !== "number" || Number.isNaN(childrenCount)) return false;
20
+ return childrenCount === -1 || childrenCount > 0;
21
+ }
22
+ /**
23
+ * Convert AFS entry to explorer entry
24
+ *
25
+ * Type determination priority:
26
+ * 1. afs:executable → exec
27
+ * 2. afs:link → link
28
+ * 3. childrenCount > 0 or -1 → directory
29
+ * 4. Otherwise (childrenCount = 0, undefined, invalid) → file
30
+ */
31
+ function toExplorerEntry(entry, _basePath) {
32
+ const name = entry.path.split("/").pop() || entry.path;
33
+ const metadata = entry.meta || {};
34
+ let type = "file";
35
+ if (isExecutable(metadata)) type = "exec";
36
+ else if (metadata.kind === "afs:link" || metadata.kinds?.includes("afs:link")) type = "link";
37
+ else if (isDirectory(metadata.childrenCount)) type = "directory";
38
+ return {
39
+ name,
40
+ path: entry.path,
41
+ type,
42
+ size: metadata.size,
43
+ modified: entry.updatedAt instanceof Date ? entry.updatedAt : void 0,
44
+ childrenCount: metadata.childrenCount,
45
+ hash: metadata.hash,
46
+ description: metadata.description,
47
+ provider: metadata.provider,
48
+ icon: metadata.icon,
49
+ kind: metadata.kind,
50
+ kinds: Array.isArray(metadata.kinds) ? metadata.kinds : void 0,
51
+ label: metadata.label,
52
+ tags: Array.isArray(metadata.tags) ? metadata.tags : void 0,
53
+ actions: entry.actions
54
+ };
55
+ }
56
+ /**
57
+ * Create parent directory entry
58
+ */
59
+ function createUpEntry(parentPath) {
60
+ return {
61
+ name: "..",
62
+ path: parentPath,
63
+ type: "up"
64
+ };
65
+ }
66
+ /**
67
+ * Build immediate children from a list of deep paths
68
+ * For example, if path is "/" and entries contain "/github/ArcBlock/afs",
69
+ * this returns a virtual directory entry for "/github"
70
+ */
71
+ function buildImmediateChildren(path, afsEntries) {
72
+ const normalizedPath = path === "/" ? "" : path;
73
+ const pathDepth = normalizedPath === "" ? 0 : normalizedPath.split("/").filter(Boolean).length;
74
+ const childrenMap = /* @__PURE__ */ new Map();
75
+ for (const entry of afsEntries) {
76
+ if (entry.path === path) continue;
77
+ const entryParts = entry.path.split("/").filter(Boolean);
78
+ const childName = entryParts[pathDepth];
79
+ if (!childName) continue;
80
+ const childPath = `/${entryParts.slice(0, pathDepth + 1).join("/")}`;
81
+ if (entryParts.length === pathDepth + 1) childrenMap.set(childName, toExplorerEntry(entry, path));
82
+ else if (!childrenMap.has(childName)) childrenMap.set(childName, {
83
+ name: childName,
84
+ path: childPath,
85
+ type: "directory",
86
+ childrenCount: -1
87
+ });
88
+ }
89
+ return childrenMap;
90
+ }
91
+ /**
92
+ * Load directory entries from AFS
93
+ */
94
+ async function loadDirectory(afs, path) {
95
+ try {
96
+ const result = await afs.list(path, { maxDepth: 1 });
97
+ const entries = [];
98
+ if (path !== "/") {
99
+ const parentPath = path.split("/").slice(0, -1).join("/") || "/";
100
+ entries.push(createUpEntry(parentPath));
101
+ }
102
+ const childrenMap = buildImmediateChildren(path, result.data);
103
+ entries.push(...childrenMap.values());
104
+ entries.sort((a, b) => {
105
+ if (a.type === "up") return -1;
106
+ if (b.type === "up") return 1;
107
+ const aIsDir = a.type === "directory";
108
+ const bIsDir = b.type === "directory";
109
+ if (aIsDir && !bIsDir) return -1;
110
+ if (!aIsDir && bIsDir) return 1;
111
+ return a.name.localeCompare(b.name);
112
+ });
113
+ return { entries };
114
+ } catch (error) {
115
+ return {
116
+ entries: [],
117
+ error: error instanceof Error ? error.message : "Failed to load directory"
118
+ };
119
+ }
120
+ }
121
+ /**
122
+ * Load metadata for an entry using stat() to get enriched data (including actions)
123
+ */
124
+ async function loadMetadata(afs, entry) {
125
+ if (entry.type === "up") return;
126
+ try {
127
+ const data = (await afs.stat(entry.path)).data;
128
+ if (!data) return {
129
+ path: entry.path,
130
+ size: entry.size,
131
+ modified: entry.modified
132
+ };
133
+ const meta = data.meta || {};
134
+ return {
135
+ path: entry.path,
136
+ size: meta.size,
137
+ modified: data.updatedAt instanceof Date ? data.updatedAt : void 0,
138
+ childrenCount: meta.childrenCount,
139
+ hash: meta.hash,
140
+ description: meta.description,
141
+ provider: meta.provider,
142
+ mountPath: meta.mountPath,
143
+ uri: meta.uri,
144
+ permissions: meta.permissions,
145
+ actions: data.actions,
146
+ extra: meta
147
+ };
148
+ } catch {
149
+ return {
150
+ path: entry.path,
151
+ size: entry.size,
152
+ modified: entry.modified
153
+ };
154
+ }
155
+ }
156
+ /**
157
+ * Get explain output for an entry
158
+ */
159
+ async function getExplain(afs, path) {
160
+ try {
161
+ const entry = (await afs.list(path, { maxDepth: 0 })).data[0];
162
+ if (!entry) return {
163
+ content: "",
164
+ error: "Entry not found"
165
+ };
166
+ const metadata = entry.meta || {};
167
+ const lines = [];
168
+ lines.push(`OBJECT ${path}`);
169
+ lines.push("");
170
+ if (metadata.description) {
171
+ lines.push("DESCRIPTION");
172
+ lines.push(metadata.description);
173
+ lines.push("");
174
+ }
175
+ if (metadata.size !== void 0) {
176
+ lines.push("SIZE");
177
+ lines.push(`${metadata.size} bytes`);
178
+ lines.push("");
179
+ }
180
+ if (metadata.childrenCount !== void 0) {
181
+ lines.push("CHILDREN");
182
+ lines.push(`${metadata.childrenCount} items`);
183
+ lines.push("");
184
+ }
185
+ if (metadata.provider) {
186
+ lines.push("PROVIDER");
187
+ lines.push(metadata.provider);
188
+ lines.push("");
189
+ }
190
+ if (metadata.hash) {
191
+ lines.push("HASH");
192
+ lines.push(metadata.hash);
193
+ lines.push("");
194
+ }
195
+ return { content: lines.join("\n") };
196
+ } catch (error) {
197
+ return {
198
+ content: "",
199
+ error: error instanceof Error ? error.message : "Failed to get explain"
200
+ };
201
+ }
202
+ }
203
+ /**
204
+ * Execute an action on an entry
205
+ *
206
+ * For executable entries (kinds includes "afs:executable"), this calls
207
+ * the underlying exec command. The action parameter is currently not used
208
+ * but reserved for future action selection.
209
+ */
210
+ async function executeAction(afs, path, _action, params) {
211
+ try {
212
+ const result = await afs.exec(path, params || {}, {});
213
+ return {
214
+ success: result.success,
215
+ message: result.error?.message,
216
+ data: result.data
217
+ };
218
+ } catch (error) {
219
+ return {
220
+ success: false,
221
+ message: error instanceof Error ? error.message : String(error)
222
+ };
223
+ }
224
+ }
225
+ /**
226
+ * Read file content
227
+ */
228
+ async function readFileContent(afs, path) {
229
+ try {
230
+ const entry = (await afs.read(path)).data;
231
+ if (!entry) return {
232
+ content: "",
233
+ error: "File not found"
234
+ };
235
+ const rawContent = entry.content;
236
+ if (rawContent === void 0 || rawContent === null) return {
237
+ content: "",
238
+ error: "No content available"
239
+ };
240
+ let content;
241
+ if (typeof rawContent === "string") content = rawContent;
242
+ else if (Buffer.isBuffer(rawContent)) content = rawContent.toString("utf-8");
243
+ else if (rawContent instanceof Uint8Array) content = Buffer.from(rawContent).toString("utf-8");
244
+ else if (rawContent instanceof ArrayBuffer) content = Buffer.from(new Uint8Array(rawContent)).toString("utf-8");
245
+ else if (typeof rawContent === "object") content = JSON.stringify(rawContent, null, 2);
246
+ else content = String(rawContent);
247
+ return { content };
248
+ } catch (error) {
249
+ return {
250
+ content: "",
251
+ error: error instanceof Error ? error.message : "Failed to read file"
252
+ };
253
+ }
254
+ }
255
+ /**
256
+ * Navigation helpers
257
+ */
258
+ const navigation = {
259
+ up(state) {
260
+ return { selectedIndex: Math.max(0, state.selectedIndex - 1) };
261
+ },
262
+ down(state) {
263
+ return { selectedIndex: Math.min(state.entries.length - 1, state.selectedIndex + 1) };
264
+ },
265
+ home(_state) {
266
+ return {
267
+ selectedIndex: 0,
268
+ scrollOffset: 0
269
+ };
270
+ },
271
+ end(state) {
272
+ return { selectedIndex: Math.max(0, state.entries.length - 1) };
273
+ },
274
+ pageUp(state, pageSize) {
275
+ return { selectedIndex: Math.max(0, state.selectedIndex - pageSize) };
276
+ },
277
+ pageDown(state, pageSize) {
278
+ return { selectedIndex: Math.min(state.entries.length - 1, state.selectedIndex + pageSize) };
279
+ },
280
+ getSelected(state) {
281
+ return state.entries[state.selectedIndex];
282
+ },
283
+ getParentPath(path) {
284
+ if (path === "/") return "/";
285
+ const parts = path.split("/").filter(Boolean);
286
+ parts.pop();
287
+ return `/${parts.join("/")}` || "/";
288
+ }
289
+ };
290
+ /**
291
+ * Create initial state
292
+ */
293
+ function createInitialState(startPath = "/") {
294
+ return {
295
+ currentPath: startPath,
296
+ entries: [],
297
+ selectedIndex: 0,
298
+ scrollOffset: 0,
299
+ loading: true
300
+ };
301
+ }
302
+
303
+ //#endregion
304
+ export { createInitialState, executeAction, getExplain, loadDirectory, loadMetadata, navigation, readFileContent };
305
+ //# sourceMappingURL=actions.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions.mjs","names":[],"sources":["../../src/explorer/actions.ts"],"sourcesContent":["/**\n * AFS Explorer Actions\n *\n * Core action handlers for the explorer.\n * These are separated from UI for testability.\n */\n\nimport type { AFS, AFSEntry, AFSEntryMetadata } from \"@aigne/afs\";\nimport type { ActionResult, EntryMetadata, ExplorerEntry, ExplorerState } from \"./types.js\";\n\n/**\n * Check if an entry is executable based on its meta\n */\nexport function isExecutable(meta: AFSEntryMetadata | null | undefined): boolean {\n if (!meta) return false;\n\n if (Array.isArray(meta.kinds)) {\n return meta.kinds.includes(\"afs:executable\");\n }\n\n return meta.kind === \"afs:executable\";\n}\n\n/**\n * Check if a value is a valid directory-indicating childrenCount\n *\n * Per Provider Protocol spec:\n * - childrenCount > 0: known children → directory\n * - childrenCount = -1: unknown children count → directory\n * - childrenCount = 0 or undefined: leaf node → file\n */\nfunction isDirectory(childrenCount: unknown): boolean {\n if (typeof childrenCount !== \"number\" || Number.isNaN(childrenCount)) {\n return false;\n }\n // -1 means unknown children (directory), > 0 means has children (directory)\n return childrenCount === -1 || childrenCount > 0;\n}\n\n/**\n * Convert AFS entry to explorer entry\n *\n * Type determination priority:\n * 1. afs:executable → exec\n * 2. afs:link → link\n * 3. childrenCount > 0 or -1 → directory\n * 4. Otherwise (childrenCount = 0, undefined, invalid) → file\n */\nexport function toExplorerEntry(entry: AFSEntry, _basePath: string): ExplorerEntry {\n const name = entry.path.split(\"/\").pop() || entry.path;\n const metadata = entry.meta || {};\n\n // Determine type based on childrenCount (per Provider Protocol spec)\n // - childrenCount > 0 or -1 → directory\n // - childrenCount = 0, undefined, or invalid → file (leaf node)\n let type: ExplorerEntry[\"type\"] = \"file\"; // Default to file (leaf node)\n\n // Check for special types from kinds array or kind field (highest priority)\n if (isExecutable(metadata)) {\n type = \"exec\";\n } else if (metadata.kind === \"afs:link\" || metadata.kinds?.includes(\"afs:link\")) {\n type = \"link\";\n } else if (isDirectory(metadata.childrenCount)) {\n type = \"directory\";\n }\n // Otherwise keep default \"file\" - leaf node\n\n return {\n name,\n path: entry.path,\n type,\n size: metadata.size,\n modified: entry.updatedAt instanceof Date ? entry.updatedAt : undefined,\n childrenCount: metadata.childrenCount,\n hash: metadata.hash,\n description: metadata.description,\n provider: metadata.provider,\n // Meta fields from .afs/meta.yaml\n icon: metadata.icon,\n kind: metadata.kind,\n kinds: Array.isArray(metadata.kinds) ? metadata.kinds : undefined,\n label: metadata.label,\n tags: Array.isArray(metadata.tags) ? metadata.tags : undefined,\n // Actions from AFSEntry\n actions: entry.actions,\n };\n}\n\n/**\n * Create parent directory entry\n */\nexport function createUpEntry(parentPath: string): ExplorerEntry {\n return {\n name: \"..\",\n path: parentPath,\n type: \"up\",\n };\n}\n\n/**\n * Build immediate children from a list of deep paths\n * For example, if path is \"/\" and entries contain \"/github/ArcBlock/afs\",\n * this returns a virtual directory entry for \"/github\"\n */\nfunction buildImmediateChildren(path: string, afsEntries: AFSEntry[]): Map<string, ExplorerEntry> {\n const normalizedPath = path === \"/\" ? \"\" : path;\n const pathDepth = normalizedPath === \"\" ? 0 : normalizedPath.split(\"/\").filter(Boolean).length;\n const childrenMap = new Map<string, ExplorerEntry>();\n\n for (const entry of afsEntries) {\n // Skip the current directory itself\n if (entry.path === path) continue;\n\n const entryParts = entry.path.split(\"/\").filter(Boolean);\n\n // Get the immediate child name (the part right after current path)\n const childName = entryParts[pathDepth];\n if (!childName) continue;\n\n const childPath = `/${entryParts.slice(0, pathDepth + 1).join(\"/\")}`;\n\n // Check if this entry IS the immediate child (not a deeper descendant)\n const isDirectChild = entryParts.length === pathDepth + 1;\n\n if (isDirectChild) {\n // This is a direct child - use the actual entry\n childrenMap.set(childName, toExplorerEntry(entry, path));\n } else if (!childrenMap.has(childName)) {\n // This is a deeper descendant - create a virtual directory for the intermediate path\n childrenMap.set(childName, {\n name: childName,\n path: childPath,\n type: \"directory\",\n childrenCount: -1, // Unknown\n });\n }\n }\n\n return childrenMap;\n}\n\n/**\n * Load directory entries from AFS\n */\nexport async function loadDirectory(\n afs: AFS,\n path: string,\n): Promise<{ entries: ExplorerEntry[]; error?: string }> {\n try {\n const result = await afs.list(path, { maxDepth: 1 });\n const entries: ExplorerEntry[] = [];\n\n // Add parent directory entry if not at root\n if (path !== \"/\") {\n const parentPath = path.split(\"/\").slice(0, -1).join(\"/\") || \"/\";\n entries.push(createUpEntry(parentPath));\n }\n\n // Build immediate children from potentially deep paths\n const childrenMap = buildImmediateChildren(path, result.data);\n entries.push(...childrenMap.values());\n\n // Sort: directories first, then by name\n entries.sort((a, b) => {\n // Up always first\n if (a.type === \"up\") return -1;\n if (b.type === \"up\") return 1;\n\n // Directories before files\n const aIsDir = a.type === \"directory\";\n const bIsDir = b.type === \"directory\";\n if (aIsDir && !bIsDir) return -1;\n if (!aIsDir && bIsDir) return 1;\n\n // Alphabetical\n return a.name.localeCompare(b.name);\n });\n\n return { entries };\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Failed to load directory\";\n return { entries: [], error: message };\n }\n}\n\n/**\n * Load metadata for an entry using stat() to get enriched data (including actions)\n */\nexport async function loadMetadata(\n afs: AFS,\n entry: ExplorerEntry,\n): Promise<EntryMetadata | undefined> {\n if (entry.type === \"up\") {\n return undefined;\n }\n\n try {\n // Use stat to get detailed metadata with auto-enriched actions\n const result = await afs.stat(entry.path);\n const data = result.data;\n\n if (!data) {\n return {\n path: entry.path,\n size: entry.size,\n modified: entry.modified,\n };\n }\n\n const meta = data.meta || {};\n\n return {\n path: entry.path,\n size: meta.size as number | undefined,\n modified: data.updatedAt instanceof Date ? data.updatedAt : undefined,\n childrenCount: meta.childrenCount as number | undefined,\n hash: meta.hash as string | undefined,\n description: meta.description as string | undefined,\n provider: meta.provider as string | undefined,\n mountPath: meta.mountPath as string | undefined,\n uri: meta.uri as string | undefined,\n permissions: meta.permissions as string[] | undefined,\n // Actions from stat (auto-enriched by core)\n actions: data.actions,\n // All metadata fields for display\n extra: meta,\n };\n } catch {\n // Return basic metadata if detailed load fails\n return {\n path: entry.path,\n size: entry.size,\n modified: entry.modified,\n };\n }\n}\n\n/**\n * Get explain output for an entry\n */\nexport async function getExplain(\n afs: AFS,\n path: string,\n): Promise<{ content: string; error?: string }> {\n try {\n // Try to get explain from AFS if available\n // For now, build explain from metadata\n const result = await afs.list(path, { maxDepth: 0 });\n const entry = result.data[0];\n\n if (!entry) {\n return { content: \"\", error: \"Entry not found\" };\n }\n\n const metadata = entry.meta || {};\n const lines: string[] = [];\n\n lines.push(`OBJECT ${path}`);\n lines.push(\"\");\n\n if (metadata.description) {\n lines.push(\"DESCRIPTION\");\n lines.push(metadata.description);\n lines.push(\"\");\n }\n\n if (metadata.size !== undefined) {\n lines.push(\"SIZE\");\n lines.push(`${metadata.size} bytes`);\n lines.push(\"\");\n }\n\n if (metadata.childrenCount !== undefined) {\n lines.push(\"CHILDREN\");\n lines.push(`${metadata.childrenCount} items`);\n lines.push(\"\");\n }\n\n if (metadata.provider) {\n lines.push(\"PROVIDER\");\n lines.push(metadata.provider);\n lines.push(\"\");\n }\n\n if (metadata.hash) {\n lines.push(\"HASH\");\n lines.push(metadata.hash);\n lines.push(\"\");\n }\n\n return { content: lines.join(\"\\n\") };\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Failed to get explain\";\n return { content: \"\", error: message };\n }\n}\n\n/**\n * Execute an action on an entry\n *\n * For executable entries (kinds includes \"afs:executable\"), this calls\n * the underlying exec command. The action parameter is currently not used\n * but reserved for future action selection.\n */\nexport async function executeAction(\n afs: AFS,\n path: string,\n _action: string,\n params?: Record<string, unknown>,\n): Promise<ActionResult> {\n try {\n const result = await afs.exec(path, params || {}, {});\n\n return {\n success: result.success,\n message: result.error?.message,\n data: result.data,\n };\n } catch (error) {\n return {\n success: false,\n message: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\n/**\n * Read file content\n */\nexport async function readFileContent(\n afs: AFS,\n path: string,\n): Promise<{ content: string; error?: string }> {\n try {\n const result = await afs.read(path);\n const entry = result.data;\n if (!entry) {\n return { content: \"\", error: \"File not found\" };\n }\n\n // Content can be string, Buffer, ArrayBuffer, Uint8Array, or undefined\n const rawContent = entry.content;\n if (rawContent === undefined || rawContent === null) {\n return { content: \"\", error: \"No content available\" };\n }\n\n // Convert to string based on type\n let content: string;\n if (typeof rawContent === \"string\") {\n content = rawContent;\n } else if (Buffer.isBuffer(rawContent)) {\n content = rawContent.toString(\"utf-8\");\n } else if (rawContent instanceof Uint8Array) {\n content = Buffer.from(rawContent).toString(\"utf-8\");\n } else if (rawContent instanceof ArrayBuffer) {\n content = Buffer.from(new Uint8Array(rawContent)).toString(\"utf-8\");\n } else if (typeof rawContent === \"object\") {\n // If content is an object (e.g., JSON data), stringify it\n content = JSON.stringify(rawContent, null, 2);\n } else {\n // Fallback for other types\n content = String(rawContent);\n }\n\n return { content };\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Failed to read file\";\n return { content: \"\", error: message };\n }\n}\n\n/**\n * Navigation helpers\n */\nexport const navigation = {\n /**\n * Move selection up\n */\n up(state: ExplorerState): Partial<ExplorerState> {\n const newIndex = Math.max(0, state.selectedIndex - 1);\n return { selectedIndex: newIndex };\n },\n\n /**\n * Move selection down\n */\n down(state: ExplorerState): Partial<ExplorerState> {\n const newIndex = Math.min(state.entries.length - 1, state.selectedIndex + 1);\n return { selectedIndex: newIndex };\n },\n\n /**\n * Go to first item\n */\n home(_state: ExplorerState): Partial<ExplorerState> {\n return { selectedIndex: 0, scrollOffset: 0 };\n },\n\n /**\n * Go to last item\n */\n end(state: ExplorerState): Partial<ExplorerState> {\n return { selectedIndex: Math.max(0, state.entries.length - 1) };\n },\n\n /**\n * Page up (move by pageSize items)\n */\n pageUp(state: ExplorerState, pageSize: number): Partial<ExplorerState> {\n const newIndex = Math.max(0, state.selectedIndex - pageSize);\n return { selectedIndex: newIndex };\n },\n\n /**\n * Page down (move by pageSize items)\n */\n pageDown(state: ExplorerState, pageSize: number): Partial<ExplorerState> {\n const newIndex = Math.min(state.entries.length - 1, state.selectedIndex + pageSize);\n return { selectedIndex: newIndex };\n },\n\n /**\n * Get selected entry\n */\n getSelected(state: ExplorerState): ExplorerEntry | undefined {\n return state.entries[state.selectedIndex];\n },\n\n /**\n * Get parent path\n */\n getParentPath(path: string): string {\n if (path === \"/\") return \"/\";\n const parts = path.split(\"/\").filter(Boolean);\n parts.pop();\n return `/${parts.join(\"/\")}` || \"/\";\n },\n};\n\n/**\n * Filter entries by search text\n */\nexport function filterEntries(entries: ExplorerEntry[], filterText: string): ExplorerEntry[] {\n if (!filterText) return entries;\n\n const lower = filterText.toLowerCase();\n return entries.filter((e) => e.name.toLowerCase().includes(lower) || e.type === \"up\");\n}\n\n/**\n * Create initial state\n */\nexport function createInitialState(startPath: string = \"/\"): ExplorerState {\n return {\n currentPath: startPath,\n entries: [],\n selectedIndex: 0,\n scrollOffset: 0,\n loading: true,\n };\n}\n"],"mappings":"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"}