@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.
- package/README.md +262 -15
- package/dist/_virtual/rolldown_runtime.cjs +29 -0
- package/dist/_virtual/rolldown_runtime.mjs +7 -0
- package/dist/cli.cjs +40 -0
- package/dist/cli.d.cts +2 -0
- package/dist/cli.d.mts +2 -1
- package/dist/cli.mjs +36 -24
- package/dist/cli.mjs.map +1 -0
- package/dist/config/afs-loader.cjs +578 -0
- package/dist/config/afs-loader.d.cts +19 -0
- package/dist/config/afs-loader.d.cts.map +1 -0
- package/dist/config/afs-loader.d.mts +19 -0
- package/dist/config/afs-loader.d.mts.map +1 -0
- package/dist/config/afs-loader.mjs +576 -0
- package/dist/config/afs-loader.mjs.map +1 -0
- package/dist/config/env.cjs +46 -0
- package/dist/config/env.mjs +46 -0
- package/dist/config/env.mjs.map +1 -0
- package/dist/config/loader.cjs +219 -0
- package/dist/config/loader.mjs +217 -0
- package/dist/config/loader.mjs.map +1 -0
- package/dist/config/mount-commands.cjs +226 -0
- package/dist/config/mount-commands.d.cts +14 -0
- package/dist/config/mount-commands.d.cts.map +1 -0
- package/dist/config/mount-commands.d.mts +14 -0
- package/dist/config/mount-commands.d.mts.map +1 -0
- package/dist/config/mount-commands.mjs +220 -0
- package/dist/config/mount-commands.mjs.map +1 -0
- package/dist/config/schema.cjs +99 -0
- package/dist/config/schema.mjs +98 -0
- package/dist/config/schema.mjs.map +1 -0
- package/dist/core/commands/delete.cjs +41 -0
- package/dist/core/commands/delete.d.cts +18 -0
- package/dist/core/commands/delete.d.cts.map +1 -0
- package/dist/core/commands/delete.d.mts +18 -0
- package/dist/core/commands/delete.d.mts.map +1 -0
- package/dist/core/commands/delete.mjs +42 -0
- package/dist/core/commands/delete.mjs.map +1 -0
- package/dist/core/commands/exec.cjs +98 -0
- package/dist/core/commands/exec.d.cts +26 -0
- package/dist/core/commands/exec.d.cts.map +1 -0
- package/dist/core/commands/exec.d.mts +26 -0
- package/dist/core/commands/exec.d.mts.map +1 -0
- package/dist/core/commands/exec.mjs +99 -0
- package/dist/core/commands/exec.mjs.map +1 -0
- package/dist/core/commands/explain.cjs +278 -0
- package/dist/core/commands/explain.d.cts +25 -0
- package/dist/core/commands/explain.d.cts.map +1 -0
- package/dist/core/commands/explain.d.mts +25 -0
- package/dist/core/commands/explain.d.mts.map +1 -0
- package/dist/core/commands/explain.mjs +279 -0
- package/dist/core/commands/explain.mjs.map +1 -0
- package/dist/core/commands/explore.cjs +30 -0
- package/dist/core/commands/explore.d.mts +2 -0
- package/dist/core/commands/explore.mjs +31 -0
- package/dist/core/commands/explore.mjs.map +1 -0
- package/dist/core/commands/index.cjs +36 -0
- package/dist/core/commands/index.d.cts +21 -0
- package/dist/core/commands/index.d.cts.map +1 -0
- package/dist/core/commands/index.d.mts +24 -0
- package/dist/core/commands/index.d.mts.map +1 -0
- package/dist/core/commands/index.mjs +37 -0
- package/dist/core/commands/index.mjs.map +1 -0
- package/dist/core/commands/ls.cjs +57 -0
- package/dist/core/commands/ls.d.cts +21 -0
- package/dist/core/commands/ls.d.cts.map +1 -0
- package/dist/core/commands/ls.d.mts +21 -0
- package/dist/core/commands/ls.d.mts.map +1 -0
- package/dist/core/commands/ls.mjs +58 -0
- package/dist/core/commands/ls.mjs.map +1 -0
- package/dist/core/commands/mount.cjs +222 -0
- package/dist/core/commands/mount.d.cts +35 -0
- package/dist/core/commands/mount.d.cts.map +1 -0
- package/dist/core/commands/mount.d.mts +35 -0
- package/dist/core/commands/mount.d.mts.map +1 -0
- package/dist/core/commands/mount.mjs +223 -0
- package/dist/core/commands/mount.mjs.map +1 -0
- package/dist/core/commands/read.cjs +48 -0
- package/dist/core/commands/read.d.cts +17 -0
- package/dist/core/commands/read.d.cts.map +1 -0
- package/dist/core/commands/read.d.mts +17 -0
- package/dist/core/commands/read.d.mts.map +1 -0
- package/dist/core/commands/read.mjs +49 -0
- package/dist/core/commands/read.mjs.map +1 -0
- package/dist/core/commands/search.cjs +40 -0
- package/dist/core/commands/search.d.mts +2 -0
- package/dist/core/commands/search.mjs +41 -0
- package/dist/core/commands/search.mjs.map +1 -0
- package/dist/core/commands/serve.cjs +267 -0
- package/dist/core/commands/serve.d.mts +2 -0
- package/dist/core/commands/serve.mjs +267 -0
- package/dist/core/commands/serve.mjs.map +1 -0
- package/dist/core/commands/stat.cjs +53 -0
- package/dist/core/commands/stat.d.cts +17 -0
- package/dist/core/commands/stat.d.cts.map +1 -0
- package/dist/core/commands/stat.d.mts +17 -0
- package/dist/core/commands/stat.d.mts.map +1 -0
- package/dist/core/commands/stat.mjs +54 -0
- package/dist/core/commands/stat.mjs.map +1 -0
- package/dist/core/commands/types.cjs +18 -0
- package/dist/core/commands/types.d.cts +54 -0
- package/dist/core/commands/types.d.cts.map +1 -0
- package/dist/core/commands/types.d.mts +54 -0
- package/dist/core/commands/types.d.mts.map +1 -0
- package/dist/core/commands/types.mjs +19 -0
- package/dist/core/commands/types.mjs.map +1 -0
- package/dist/core/commands/write.cjs +70 -0
- package/dist/core/commands/write.d.cts +20 -0
- package/dist/core/commands/write.d.cts.map +1 -0
- package/dist/core/commands/write.d.mts +20 -0
- package/dist/core/commands/write.d.mts.map +1 -0
- package/dist/core/commands/write.mjs +71 -0
- package/dist/core/commands/write.mjs.map +1 -0
- package/dist/core/executor/index.cjs +196 -0
- package/dist/core/executor/index.d.cts +77 -0
- package/dist/core/executor/index.d.cts.map +1 -0
- package/dist/core/executor/index.d.mts +77 -0
- package/dist/core/executor/index.d.mts.map +1 -0
- package/dist/core/executor/index.mjs +195 -0
- package/dist/core/executor/index.mjs.map +1 -0
- package/dist/core/formatters/delete.cjs +37 -0
- package/dist/core/formatters/delete.d.cts +18 -0
- package/dist/core/formatters/delete.d.cts.map +1 -0
- package/dist/core/formatters/delete.d.mts +18 -0
- package/dist/core/formatters/delete.d.mts.map +1 -0
- package/dist/core/formatters/delete.mjs +37 -0
- package/dist/core/formatters/delete.mjs.map +1 -0
- package/dist/core/formatters/exec.cjs +60 -0
- package/dist/core/formatters/exec.d.cts +18 -0
- package/dist/core/formatters/exec.d.cts.map +1 -0
- package/dist/core/formatters/exec.d.mts +18 -0
- package/dist/core/formatters/exec.d.mts.map +1 -0
- package/dist/core/formatters/exec.mjs +60 -0
- package/dist/core/formatters/exec.mjs.map +1 -0
- package/dist/core/formatters/explain.cjs +99 -0
- package/dist/core/formatters/explain.d.cts +11 -0
- package/dist/core/formatters/explain.d.cts.map +1 -0
- package/dist/core/formatters/explain.d.mts +11 -0
- package/dist/core/formatters/explain.d.mts.map +1 -0
- package/dist/core/formatters/explain.mjs +98 -0
- package/dist/core/formatters/explain.mjs.map +1 -0
- package/dist/core/formatters/index.d.mts +9 -0
- package/dist/core/formatters/ls.cjs +179 -0
- package/dist/core/formatters/ls.d.cts +20 -0
- package/dist/core/formatters/ls.d.cts.map +1 -0
- package/dist/core/formatters/ls.d.mts +20 -0
- package/dist/core/formatters/ls.d.mts.map +1 -0
- package/dist/core/formatters/ls.mjs +179 -0
- package/dist/core/formatters/ls.mjs.map +1 -0
- package/dist/core/formatters/mount.cjs +55 -0
- package/dist/core/formatters/mount.d.cts +15 -0
- package/dist/core/formatters/mount.d.cts.map +1 -0
- package/dist/core/formatters/mount.d.mts +15 -0
- package/dist/core/formatters/mount.d.mts.map +1 -0
- package/dist/core/formatters/mount.mjs +55 -0
- package/dist/core/formatters/mount.mjs.map +1 -0
- package/dist/core/formatters/read.cjs +100 -0
- package/dist/core/formatters/read.d.cts +22 -0
- package/dist/core/formatters/read.d.cts.map +1 -0
- package/dist/core/formatters/read.d.mts +22 -0
- package/dist/core/formatters/read.d.mts.map +1 -0
- package/dist/core/formatters/read.mjs +100 -0
- package/dist/core/formatters/read.mjs.map +1 -0
- package/dist/core/formatters/search.cjs +44 -0
- package/dist/core/formatters/search.d.mts +1 -0
- package/dist/core/formatters/search.mjs +44 -0
- package/dist/core/formatters/search.mjs.map +1 -0
- package/dist/core/formatters/stat.cjs +155 -0
- package/dist/core/formatters/stat.d.cts +15 -0
- package/dist/core/formatters/stat.d.cts.map +1 -0
- package/dist/core/formatters/stat.d.mts +15 -0
- package/dist/core/formatters/stat.d.mts.map +1 -0
- package/dist/core/formatters/stat.mjs +155 -0
- package/dist/core/formatters/stat.mjs.map +1 -0
- package/dist/core/formatters/write.cjs +51 -0
- package/dist/core/formatters/write.d.cts +22 -0
- package/dist/core/formatters/write.d.cts.map +1 -0
- package/dist/core/formatters/write.d.mts +22 -0
- package/dist/core/formatters/write.d.mts.map +1 -0
- package/dist/core/formatters/write.mjs +51 -0
- package/dist/core/formatters/write.mjs.map +1 -0
- package/dist/core/helpers/exec-args.cjs +142 -0
- package/dist/core/helpers/exec-args.d.cts +46 -0
- package/dist/core/helpers/exec-args.d.cts.map +1 -0
- package/dist/core/helpers/exec-args.d.mts +46 -0
- package/dist/core/helpers/exec-args.d.mts.map +1 -0
- package/dist/core/helpers/exec-args.mjs +139 -0
- package/dist/core/helpers/exec-args.mjs.map +1 -0
- package/dist/core/helpers/stdin.cjs +41 -0
- package/dist/core/helpers/stdin.d.cts +15 -0
- package/dist/core/helpers/stdin.d.cts.map +1 -0
- package/dist/core/helpers/stdin.d.mts +15 -0
- package/dist/core/helpers/stdin.d.mts.map +1 -0
- package/dist/core/helpers/stdin.mjs +41 -0
- package/dist/core/helpers/stdin.mjs.map +1 -0
- package/dist/core/index.cjs +49 -0
- package/dist/core/index.d.cts +24 -0
- package/dist/core/index.d.mts +25 -0
- package/dist/core/index.mjs +24 -0
- package/dist/core/path-utils.cjs +1 -0
- package/dist/core/path-utils.mjs +3 -0
- package/dist/core/types.d.cts +24 -0
- package/dist/core/types.d.cts.map +1 -0
- package/dist/core/types.d.mts +24 -0
- package/dist/core/types.d.mts.map +1 -0
- package/dist/credential/auth-server.cjs +247 -0
- package/dist/credential/auth-server.mjs +247 -0
- package/dist/credential/auth-server.mjs.map +1 -0
- package/dist/credential/cli-auth-context.cjs +86 -0
- package/dist/credential/cli-auth-context.d.mts +1 -0
- package/dist/credential/cli-auth-context.mjs +86 -0
- package/dist/credential/cli-auth-context.mjs.map +1 -0
- package/dist/credential/index.cjs +5 -0
- package/dist/credential/index.d.mts +4 -0
- package/dist/credential/index.mjs +7 -0
- package/dist/credential/mcp-auth-context.cjs +192 -0
- package/dist/credential/mcp-auth-context.d.mts +1 -0
- package/dist/credential/mcp-auth-context.mjs +192 -0
- package/dist/credential/mcp-auth-context.mjs.map +1 -0
- package/dist/credential/resolver.cjs +127 -0
- package/dist/credential/resolver.d.mts +1 -0
- package/dist/credential/resolver.mjs +127 -0
- package/dist/credential/resolver.mjs.map +1 -0
- package/dist/credential/store.cjs +106 -0
- package/dist/credential/store.d.cts +30 -0
- package/dist/credential/store.d.cts.map +1 -0
- package/dist/credential/store.d.mts +30 -0
- package/dist/credential/store.d.mts.map +1 -0
- package/dist/credential/store.mjs +106 -0
- package/dist/credential/store.mjs.map +1 -0
- package/dist/errors.cjs +18 -0
- package/dist/errors.mjs +18 -0
- package/dist/errors.mjs.map +1 -0
- package/dist/explorer/actions.cjs +311 -0
- package/dist/explorer/actions.mjs +305 -0
- package/dist/explorer/actions.mjs.map +1 -0
- package/dist/explorer/components/dialog.cjs +508 -0
- package/dist/explorer/components/dialog.mjs +509 -0
- package/dist/explorer/components/dialog.mjs.map +1 -0
- package/dist/explorer/components/file-list.cjs +107 -0
- package/dist/explorer/components/file-list.mjs +107 -0
- package/dist/explorer/components/file-list.mjs.map +1 -0
- package/dist/explorer/components/function-bar.cjs +55 -0
- package/dist/explorer/components/function-bar.mjs +55 -0
- package/dist/explorer/components/function-bar.mjs.map +1 -0
- package/dist/explorer/components/index.cjs +5 -0
- package/dist/explorer/components/index.mjs +7 -0
- package/dist/explorer/components/metadata-panel.cjs +219 -0
- package/dist/explorer/components/metadata-panel.mjs +219 -0
- package/dist/explorer/components/metadata-panel.mjs.map +1 -0
- package/dist/explorer/components/status-bar.cjs +53 -0
- package/dist/explorer/components/status-bar.mjs +54 -0
- package/dist/explorer/components/status-bar.mjs.map +1 -0
- package/dist/explorer/keybindings.cjs +214 -0
- package/dist/explorer/keybindings.mjs +213 -0
- package/dist/explorer/keybindings.mjs.map +1 -0
- package/dist/explorer/screen.cjs +251 -0
- package/dist/explorer/screen.d.cts +23 -0
- package/dist/explorer/screen.d.cts.map +1 -0
- package/dist/explorer/screen.d.mts +23 -0
- package/dist/explorer/screen.d.mts.map +1 -0
- package/dist/explorer/screen.mjs +250 -0
- package/dist/explorer/screen.mjs.map +1 -0
- package/dist/explorer/state.cjs +53 -0
- package/dist/explorer/state.mjs +53 -0
- package/dist/explorer/state.mjs.map +1 -0
- package/dist/explorer/theme.cjs +160 -0
- package/dist/explorer/theme.mjs +157 -0
- package/dist/explorer/theme.mjs.map +1 -0
- package/dist/index.cjs +12 -0
- package/dist/index.d.cts +6 -0
- package/dist/index.d.mts +7 -4
- package/dist/index.mjs +7 -2
- package/dist/mcp/http-transport.cjs +87 -0
- package/dist/mcp/http-transport.mjs +87 -0
- package/dist/mcp/http-transport.mjs.map +1 -0
- package/dist/mcp/prompts.cjs +48 -0
- package/dist/mcp/prompts.mjs +48 -0
- package/dist/mcp/prompts.mjs.map +1 -0
- package/dist/mcp/resources.cjs +25 -0
- package/dist/mcp/resources.mjs +25 -0
- package/dist/mcp/resources.mjs.map +1 -0
- package/dist/mcp/server.cjs +74 -0
- package/dist/mcp/server.mjs +73 -0
- package/dist/mcp/server.mjs.map +1 -0
- package/dist/mcp/tools.cjs +152 -0
- package/dist/mcp/tools.mjs +152 -0
- package/dist/mcp/tools.mjs.map +1 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/index.d.cts +10 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/index.d.cts.map +1 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/index.d.mts +10 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/index.d.mts.map +1 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/types.d.cts +46 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/types.d.cts.map +1 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/types.d.mts +46 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/types.d.mts.map +1 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/urlpattern.cjs +902 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/urlpattern.mjs +902 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/dist/urlpattern.mjs.map +1 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/index.cjs +6 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/index.mjs +8 -0
- package/dist/node_modules/.pnpm/urlpattern-polyfill@10.1.0/node_modules/urlpattern-polyfill/index.mjs.map +1 -0
- package/dist/path-utils.cjs +105 -0
- package/dist/path-utils.d.cts +50 -0
- package/dist/path-utils.d.cts.map +1 -0
- package/dist/path-utils.d.mts +50 -0
- package/dist/path-utils.d.mts.map +1 -0
- package/dist/path-utils.mjs +104 -0
- package/dist/path-utils.mjs.map +1 -0
- package/dist/repl.cjs +491 -0
- package/dist/repl.d.cts +15 -0
- package/dist/repl.d.cts.map +1 -0
- package/dist/repl.d.mts +16 -0
- package/dist/repl.d.mts.map +1 -0
- package/dist/repl.mjs +491 -0
- package/dist/repl.mjs.map +1 -0
- package/dist/serve.cjs +146 -0
- package/dist/serve.d.cts +41 -0
- package/dist/serve.d.cts.map +1 -0
- package/dist/serve.d.mts +41 -0
- package/dist/serve.d.mts.map +1 -0
- package/dist/serve.mjs +146 -0
- package/dist/serve.mjs.map +1 -0
- package/dist/ui/header.cjs +12 -0
- package/dist/ui/header.mjs +13 -0
- package/dist/ui/header.mjs.map +1 -0
- package/dist/ui/index.cjs +8 -0
- package/dist/ui/index.mjs +9 -0
- package/dist/ui/index.mjs.map +1 -0
- package/dist/ui/terminal.cjs +88 -0
- package/dist/ui/terminal.mjs +88 -0
- package/dist/ui/terminal.mjs.map +1 -0
- package/dist/version.cjs +9 -0
- package/dist/version.d.cts +5 -0
- package/dist/version.d.cts.map +1 -0
- package/dist/version.d.mts +5 -0
- package/dist/version.d.mts.map +1 -0
- package/dist/version.mjs +9 -0
- package/dist/version.mjs.map +1 -0
- package/package.json +77 -11
- package/.turbo/turbo-build.log +0 -18
- package/.turbo/turbo-check-types.log +0 -4
- package/dist/version--p6A8sKX.mjs +0 -5
- package/src/cli.test.ts +0 -8
- package/src/cli.ts +0 -29
- package/src/index.ts +0 -7
- package/src/version.ts +0 -1
- 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":";;;;AAaA,SAAgB,aAAa,MAAoD;AAC/E,KAAI,CAAC,KAAM,QAAO;AAElB,KAAI,MAAM,QAAQ,KAAK,MAAM,CAC3B,QAAO,KAAK,MAAM,SAAS,iBAAiB;AAG9C,QAAO,KAAK,SAAS;;;;;;;;;;AAWvB,SAAS,YAAY,eAAiC;AACpD,KAAI,OAAO,kBAAkB,YAAY,OAAO,MAAM,cAAc,CAClE,QAAO;AAGT,QAAO,kBAAkB,MAAM,gBAAgB;;;;;;;;;;;AAYjD,SAAgB,gBAAgB,OAAiB,WAAkC;CACjF,MAAM,OAAO,MAAM,KAAK,MAAM,IAAI,CAAC,KAAK,IAAI,MAAM;CAClD,MAAM,WAAW,MAAM,QAAQ,EAAE;CAKjC,IAAI,OAA8B;AAGlC,KAAI,aAAa,SAAS,CACxB,QAAO;UACE,SAAS,SAAS,cAAc,SAAS,OAAO,SAAS,WAAW,CAC7E,QAAO;UACE,YAAY,SAAS,cAAc,CAC5C,QAAO;AAIT,QAAO;EACL;EACA,MAAM,MAAM;EACZ;EACA,MAAM,SAAS;EACf,UAAU,MAAM,qBAAqB,OAAO,MAAM,YAAY;EAC9D,eAAe,SAAS;EACxB,MAAM,SAAS;EACf,aAAa,SAAS;EACtB,UAAU,SAAS;EAEnB,MAAM,SAAS;EACf,MAAM,SAAS;EACf,OAAO,MAAM,QAAQ,SAAS,MAAM,GAAG,SAAS,QAAQ;EACxD,OAAO,SAAS;EAChB,MAAM,MAAM,QAAQ,SAAS,KAAK,GAAG,SAAS,OAAO;EAErD,SAAS,MAAM;EAChB;;;;;AAMH,SAAgB,cAAc,YAAmC;AAC/D,QAAO;EACL,MAAM;EACN,MAAM;EACN,MAAM;EACP;;;;;;;AAQH,SAAS,uBAAuB,MAAc,YAAoD;CAChG,MAAM,iBAAiB,SAAS,MAAM,KAAK;CAC3C,MAAM,YAAY,mBAAmB,KAAK,IAAI,eAAe,MAAM,IAAI,CAAC,OAAO,QAAQ,CAAC;CACxF,MAAM,8BAAc,IAAI,KAA4B;AAEpD,MAAK,MAAM,SAAS,YAAY;AAE9B,MAAI,MAAM,SAAS,KAAM;EAEzB,MAAM,aAAa,MAAM,KAAK,MAAM,IAAI,CAAC,OAAO,QAAQ;EAGxD,MAAM,YAAY,WAAW;AAC7B,MAAI,CAAC,UAAW;EAEhB,MAAM,YAAY,IAAI,WAAW,MAAM,GAAG,YAAY,EAAE,CAAC,KAAK,IAAI;AAKlE,MAFsB,WAAW,WAAW,YAAY,EAItD,aAAY,IAAI,WAAW,gBAAgB,OAAO,KAAK,CAAC;WAC/C,CAAC,YAAY,IAAI,UAAU,CAEpC,aAAY,IAAI,WAAW;GACzB,MAAM;GACN,MAAM;GACN,MAAM;GACN,eAAe;GAChB,CAAC;;AAIN,QAAO;;;;;AAMT,eAAsB,cACpB,KACA,MACuD;AACvD,KAAI;EACF,MAAM,SAAS,MAAM,IAAI,KAAK,MAAM,EAAE,UAAU,GAAG,CAAC;EACpD,MAAM,UAA2B,EAAE;AAGnC,MAAI,SAAS,KAAK;GAChB,MAAM,aAAa,KAAK,MAAM,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,KAAK,IAAI,IAAI;AAC7D,WAAQ,KAAK,cAAc,WAAW,CAAC;;EAIzC,MAAM,cAAc,uBAAuB,MAAM,OAAO,KAAK;AAC7D,UAAQ,KAAK,GAAG,YAAY,QAAQ,CAAC;AAGrC,UAAQ,MAAM,GAAG,MAAM;AAErB,OAAI,EAAE,SAAS,KAAM,QAAO;AAC5B,OAAI,EAAE,SAAS,KAAM,QAAO;GAG5B,MAAM,SAAS,EAAE,SAAS;GAC1B,MAAM,SAAS,EAAE,SAAS;AAC1B,OAAI,UAAU,CAAC,OAAQ,QAAO;AAC9B,OAAI,CAAC,UAAU,OAAQ,QAAO;AAG9B,UAAO,EAAE,KAAK,cAAc,EAAE,KAAK;IACnC;AAEF,SAAO,EAAE,SAAS;UACX,OAAO;AAEd,SAAO;GAAE,SAAS,EAAE;GAAE,OADN,iBAAiB,QAAQ,MAAM,UAAU;GACnB;;;;;;AAO1C,eAAsB,aACpB,KACA,OACoC;AACpC,KAAI,MAAM,SAAS,KACjB;AAGF,KAAI;EAGF,MAAM,QADS,MAAM,IAAI,KAAK,MAAM,KAAK,EACrB;AAEpB,MAAI,CAAC,KACH,QAAO;GACL,MAAM,MAAM;GACZ,MAAM,MAAM;GACZ,UAAU,MAAM;GACjB;EAGH,MAAM,OAAO,KAAK,QAAQ,EAAE;AAE5B,SAAO;GACL,MAAM,MAAM;GACZ,MAAM,KAAK;GACX,UAAU,KAAK,qBAAqB,OAAO,KAAK,YAAY;GAC5D,eAAe,KAAK;GACpB,MAAM,KAAK;GACX,aAAa,KAAK;GAClB,UAAU,KAAK;GACf,WAAW,KAAK;GAChB,KAAK,KAAK;GACV,aAAa,KAAK;GAElB,SAAS,KAAK;GAEd,OAAO;GACR;SACK;AAEN,SAAO;GACL,MAAM,MAAM;GACZ,MAAM,MAAM;GACZ,UAAU,MAAM;GACjB;;;;;;AAOL,eAAsB,WACpB,KACA,MAC8C;AAC9C,KAAI;EAIF,MAAM,SADS,MAAM,IAAI,KAAK,MAAM,EAAE,UAAU,GAAG,CAAC,EAC/B,KAAK;AAE1B,MAAI,CAAC,MACH,QAAO;GAAE,SAAS;GAAI,OAAO;GAAmB;EAGlD,MAAM,WAAW,MAAM,QAAQ,EAAE;EACjC,MAAM,QAAkB,EAAE;AAE1B,QAAM,KAAK,UAAU,OAAO;AAC5B,QAAM,KAAK,GAAG;AAEd,MAAI,SAAS,aAAa;AACxB,SAAM,KAAK,cAAc;AACzB,SAAM,KAAK,SAAS,YAAY;AAChC,SAAM,KAAK,GAAG;;AAGhB,MAAI,SAAS,SAAS,QAAW;AAC/B,SAAM,KAAK,OAAO;AAClB,SAAM,KAAK,GAAG,SAAS,KAAK,QAAQ;AACpC,SAAM,KAAK,GAAG;;AAGhB,MAAI,SAAS,kBAAkB,QAAW;AACxC,SAAM,KAAK,WAAW;AACtB,SAAM,KAAK,GAAG,SAAS,cAAc,QAAQ;AAC7C,SAAM,KAAK,GAAG;;AAGhB,MAAI,SAAS,UAAU;AACrB,SAAM,KAAK,WAAW;AACtB,SAAM,KAAK,SAAS,SAAS;AAC7B,SAAM,KAAK,GAAG;;AAGhB,MAAI,SAAS,MAAM;AACjB,SAAM,KAAK,OAAO;AAClB,SAAM,KAAK,SAAS,KAAK;AACzB,SAAM,KAAK,GAAG;;AAGhB,SAAO,EAAE,SAAS,MAAM,KAAK,KAAK,EAAE;UAC7B,OAAO;AAEd,SAAO;GAAE,SAAS;GAAI,OADN,iBAAiB,QAAQ,MAAM,UAAU;GACnB;;;;;;;;;;AAW1C,eAAsB,cACpB,KACA,MACA,SACA,QACuB;AACvB,KAAI;EACF,MAAM,SAAS,MAAM,IAAI,KAAK,MAAM,UAAU,EAAE,EAAE,EAAE,CAAC;AAErD,SAAO;GACL,SAAS,OAAO;GAChB,SAAS,OAAO,OAAO;GACvB,MAAM,OAAO;GACd;UACM,OAAO;AACd,SAAO;GACL,SAAS;GACT,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;GAChE;;;;;;AAOL,eAAsB,gBACpB,KACA,MAC8C;AAC9C,KAAI;EAEF,MAAM,SADS,MAAM,IAAI,KAAK,KAAK,EACd;AACrB,MAAI,CAAC,MACH,QAAO;GAAE,SAAS;GAAI,OAAO;GAAkB;EAIjD,MAAM,aAAa,MAAM;AACzB,MAAI,eAAe,UAAa,eAAe,KAC7C,QAAO;GAAE,SAAS;GAAI,OAAO;GAAwB;EAIvD,IAAI;AACJ,MAAI,OAAO,eAAe,SACxB,WAAU;WACD,OAAO,SAAS,WAAW,CACpC,WAAU,WAAW,SAAS,QAAQ;WAC7B,sBAAsB,WAC/B,WAAU,OAAO,KAAK,WAAW,CAAC,SAAS,QAAQ;WAC1C,sBAAsB,YAC/B,WAAU,OAAO,KAAK,IAAI,WAAW,WAAW,CAAC,CAAC,SAAS,QAAQ;WAC1D,OAAO,eAAe,SAE/B,WAAU,KAAK,UAAU,YAAY,MAAM,EAAE;MAG7C,WAAU,OAAO,WAAW;AAG9B,SAAO,EAAE,SAAS;UACX,OAAO;AAEd,SAAO;GAAE,SAAS;GAAI,OADN,iBAAiB,QAAQ,MAAM,UAAU;GACnB;;;;;;AAO1C,MAAa,aAAa;CAIxB,GAAG,OAA8C;AAE/C,SAAO,EAAE,eADQ,KAAK,IAAI,GAAG,MAAM,gBAAgB,EAAE,EACnB;;CAMpC,KAAK,OAA8C;AAEjD,SAAO,EAAE,eADQ,KAAK,IAAI,MAAM,QAAQ,SAAS,GAAG,MAAM,gBAAgB,EAAE,EAC1C;;CAMpC,KAAK,QAA+C;AAClD,SAAO;GAAE,eAAe;GAAG,cAAc;GAAG;;CAM9C,IAAI,OAA8C;AAChD,SAAO,EAAE,eAAe,KAAK,IAAI,GAAG,MAAM,QAAQ,SAAS,EAAE,EAAE;;CAMjE,OAAO,OAAsB,UAA0C;AAErE,SAAO,EAAE,eADQ,KAAK,IAAI,GAAG,MAAM,gBAAgB,SAAS,EAC1B;;CAMpC,SAAS,OAAsB,UAA0C;AAEvE,SAAO,EAAE,eADQ,KAAK,IAAI,MAAM,QAAQ,SAAS,GAAG,MAAM,gBAAgB,SAAS,EACjD;;CAMpC,YAAY,OAAiD;AAC3D,SAAO,MAAM,QAAQ,MAAM;;CAM7B,cAAc,MAAsB;AAClC,MAAI,SAAS,IAAK,QAAO;EACzB,MAAM,QAAQ,KAAK,MAAM,IAAI,CAAC,OAAO,QAAQ;AAC7C,QAAM,KAAK;AACX,SAAO,IAAI,MAAM,KAAK,IAAI,MAAM;;CAEnC;;;;AAeD,SAAgB,mBAAmB,YAAoB,KAAoB;AACzE,QAAO;EACL,aAAa;EACb,SAAS,EAAE;EACX,eAAe;EACf,cAAc;EACd,SAAS;EACV"}
|