@aigne/afs-cli 1.11.0-beta.3 → 1.11.0-beta.4
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 +209 -31
- package/dist/cli.cjs +107 -25
- package/dist/cli.mjs +107 -25
- package/dist/cli.mjs.map +1 -1
- package/dist/commands/ls.cjs +6 -4
- package/dist/commands/ls.mjs +7 -4
- package/dist/commands/ls.mjs.map +1 -1
- package/dist/commands/mount.cjs +38 -15
- package/dist/commands/mount.mjs +38 -15
- package/dist/commands/mount.mjs.map +1 -1
- package/dist/commands/serve.cjs +6 -5
- package/dist/commands/serve.mjs +6 -5
- package/dist/commands/serve.mjs.map +1 -1
- package/dist/config/loader.cjs +14 -3
- package/dist/config/loader.mjs +14 -3
- package/dist/config/loader.mjs.map +1 -1
- package/dist/config/schema.cjs +55 -2
- package/dist/config/schema.mjs +54 -2
- package/dist/config/schema.mjs.map +1 -1
- package/dist/explorer/actions.cjs +246 -0
- package/dist/explorer/actions.mjs +240 -0
- package/dist/explorer/actions.mjs.map +1 -0
- package/dist/explorer/components/dialog.cjs +231 -0
- package/dist/explorer/components/dialog.mjs +232 -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 +122 -0
- package/dist/explorer/components/metadata-panel.mjs +122 -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 +200 -0
- package/dist/explorer/screen.mjs +199 -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 +158 -0
- package/dist/explorer/theme.mjs +155 -0
- package/dist/explorer/theme.mjs.map +1 -0
- package/dist/path-utils.cjs +104 -0
- package/dist/path-utils.mjs +104 -0
- package/dist/path-utils.mjs.map +1 -0
- package/dist/runtime.cjs +47 -33
- package/dist/runtime.mjs +47 -33
- package/dist/runtime.mjs.map +1 -1
- package/dist/ui/header.cjs +60 -0
- package/dist/ui/header.mjs +59 -0
- package/dist/ui/header.mjs.map +1 -0
- package/dist/ui/index.cjs +17 -0
- package/dist/ui/index.mjs +15 -0
- package/dist/ui/index.mjs.map +1 -0
- package/dist/ui/terminal.cjs +97 -0
- package/dist/ui/terminal.mjs +95 -0
- package/dist/ui/terminal.mjs.map +1 -0
- package/package.json +9 -7
package/dist/cli.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { VERSION } from "./version.mjs";
|
|
3
3
|
import { execCommand, formatExecOutput } from "./commands/exec.mjs";
|
|
4
|
+
import { ConfigLoader } from "./config/loader.mjs";
|
|
5
|
+
import { colors, printHeader, shouldShowHeader } from "./ui/index.mjs";
|
|
4
6
|
import { formatMountListOutput, mountAddCommand, mountListCommand, mountRemoveCommand, mountValidateCommand } from "./commands/mount.mjs";
|
|
5
7
|
import { explainCommand, explainPathCommand, formatExplainOutput, formatPathExplainOutput } from "./commands/explain.mjs";
|
|
6
8
|
import { formatLsOutput, lsCommand } from "./commands/ls.mjs";
|
|
@@ -11,6 +13,7 @@ import { formatStatOutput, statCommand } from "./commands/stat.mjs";
|
|
|
11
13
|
import { formatWriteOutput, writeCommand } from "./commands/write.mjs";
|
|
12
14
|
import "./commands/index.mjs";
|
|
13
15
|
import { CLIError, ExitCode } from "./errors.mjs";
|
|
16
|
+
import { createExplorerScreen } from "./explorer/screen.mjs";
|
|
14
17
|
import yargs from "yargs";
|
|
15
18
|
import { hideBin } from "yargs/helpers";
|
|
16
19
|
|
|
@@ -29,19 +32,58 @@ import { hideBin } from "yargs/helpers";
|
|
|
29
32
|
* - afs write <path> Write file content (--content or stdin)
|
|
30
33
|
* - afs exec <path> <action> Execute operation
|
|
31
34
|
* - afs serve Start HTTP server to expose AFS
|
|
35
|
+
* - afs explore [path] Interactive TUI file explorer
|
|
32
36
|
*
|
|
33
|
-
* Output modes:
|
|
34
|
-
* -
|
|
37
|
+
* Output modes (default: human):
|
|
38
|
+
* - --view=human: Human friendly format with colors (default)
|
|
39
|
+
* - --view=llm: LLM optimized output (token-efficient)
|
|
40
|
+
* - --view=default: Machine truth (script/pipe friendly)
|
|
35
41
|
* - --json: Structured JSON
|
|
36
|
-
* - --view=llm: LLM optimized output
|
|
37
|
-
* - --view=human: Human friendly format
|
|
38
42
|
*/
|
|
39
43
|
function getViewType(argv) {
|
|
40
44
|
if (argv.json) return "json";
|
|
41
45
|
if (argv.view) return argv.view;
|
|
42
|
-
return "
|
|
46
|
+
return "human";
|
|
47
|
+
}
|
|
48
|
+
let headerShown = false;
|
|
49
|
+
/**
|
|
50
|
+
* Show header if in human view mode and not already shown
|
|
51
|
+
*/
|
|
52
|
+
async function maybeShowHeader(view) {
|
|
53
|
+
if (view !== "human" || headerShown || !shouldShowHeader()) return;
|
|
54
|
+
headerShown = true;
|
|
55
|
+
try {
|
|
56
|
+
printHeader({
|
|
57
|
+
version: VERSION,
|
|
58
|
+
mountCount: (await new ConfigLoader().load(process.cwd())).mounts.length
|
|
59
|
+
});
|
|
60
|
+
} catch {
|
|
61
|
+
printHeader({
|
|
62
|
+
version: VERSION,
|
|
63
|
+
mountCount: 0
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async function showHeaderIfNeeded() {
|
|
68
|
+
const args = process.argv.slice(2);
|
|
69
|
+
if ((args.length === 0 || args.includes("--help") || args.includes("-h") || args.length === 1 && [
|
|
70
|
+
"help",
|
|
71
|
+
"mount",
|
|
72
|
+
"explain"
|
|
73
|
+
].includes(args[0])) && shouldShowHeader()) try {
|
|
74
|
+
printHeader({
|
|
75
|
+
version: VERSION,
|
|
76
|
+
mountCount: (await new ConfigLoader().load(process.cwd())).mounts.length
|
|
77
|
+
});
|
|
78
|
+
} catch {
|
|
79
|
+
printHeader({
|
|
80
|
+
version: VERSION,
|
|
81
|
+
mountCount: 0
|
|
82
|
+
});
|
|
83
|
+
}
|
|
43
84
|
}
|
|
44
85
|
async function main() {
|
|
86
|
+
await showHeaderIfNeeded();
|
|
45
87
|
const cli = yargs(hideBin(process.argv)).scriptName("afs").version(VERSION).alias("version", "V").help("help").alias("help", "h").usage("$0 <command> [options]").option("json", {
|
|
46
88
|
type: "boolean",
|
|
47
89
|
description: "Output in JSON format",
|
|
@@ -53,7 +95,8 @@ async function main() {
|
|
|
53
95
|
"llm",
|
|
54
96
|
"human"
|
|
55
97
|
],
|
|
56
|
-
|
|
98
|
+
default: "human",
|
|
99
|
+
description: "Output format (llm for AI agents, default for scripts)",
|
|
57
100
|
global: true
|
|
58
101
|
}).command(["list [path]", "ls [path]"], "List directory contents", (yargs$1) => yargs$1.positional("path", {
|
|
59
102
|
type: "string",
|
|
@@ -75,29 +118,32 @@ async function main() {
|
|
|
75
118
|
type: "string",
|
|
76
119
|
description: "Glob pattern to filter entries (e.g., *.ts, **/*.js)"
|
|
77
120
|
}), async (argv) => {
|
|
121
|
+
const view = getViewType(argv);
|
|
122
|
+
await maybeShowHeader(view);
|
|
78
123
|
const result = await lsCommand(await createRuntime(), argv.path, {
|
|
79
124
|
maxDepth: argv.depth,
|
|
80
125
|
limit: argv.limit,
|
|
81
126
|
maxChildren: argv["max-children"],
|
|
82
127
|
pattern: argv.pattern
|
|
83
128
|
});
|
|
84
|
-
const view = getViewType(argv);
|
|
85
129
|
console.log(formatLsOutput(result, view));
|
|
86
130
|
}).command("stat <path>", "Get file or directory info", (yargs$1) => yargs$1.positional("path", {
|
|
87
131
|
type: "string",
|
|
88
132
|
demandOption: true,
|
|
89
133
|
description: "Path to stat"
|
|
90
134
|
}), async (argv) => {
|
|
91
|
-
const result = await statCommand(await createRuntime(), argv.path);
|
|
92
135
|
const view = getViewType(argv);
|
|
136
|
+
await maybeShowHeader(view);
|
|
137
|
+
const result = await statCommand(await createRuntime(), argv.path);
|
|
93
138
|
console.log(formatStatOutput(result, view));
|
|
94
139
|
}).command("read <path>", "Read file content", (yargs$1) => yargs$1.positional("path", {
|
|
95
140
|
type: "string",
|
|
96
141
|
demandOption: true,
|
|
97
142
|
description: "Path to read"
|
|
98
143
|
}), async (argv) => {
|
|
99
|
-
const result = await readCommand(await createRuntime(), argv.path);
|
|
100
144
|
const view = getViewType(argv);
|
|
145
|
+
await maybeShowHeader(view);
|
|
146
|
+
const result = await readCommand(await createRuntime(), argv.path);
|
|
101
147
|
console.log(formatReadOutput(result, view));
|
|
102
148
|
}).command("write <path>", "Write content to file (from --content or stdin)", (yargs$1) => yargs$1.positional("path", {
|
|
103
149
|
type: "string",
|
|
@@ -111,6 +157,8 @@ async function main() {
|
|
|
111
157
|
default: false,
|
|
112
158
|
description: "Append to file instead of overwrite"
|
|
113
159
|
}), async (argv) => {
|
|
160
|
+
const view = getViewType(argv);
|
|
161
|
+
await maybeShowHeader(view);
|
|
114
162
|
let content;
|
|
115
163
|
if (argv.content !== void 0) content = argv.content;
|
|
116
164
|
else {
|
|
@@ -119,7 +167,6 @@ async function main() {
|
|
|
119
167
|
content = Buffer.concat(chunks).toString("utf-8");
|
|
120
168
|
}
|
|
121
169
|
const result = await writeCommand(await createRuntime(), argv.path, content, { append: argv.append });
|
|
122
|
-
const view = getViewType(argv);
|
|
123
170
|
console.log(formatWriteOutput(result, view));
|
|
124
171
|
if (!result.success) process.exit(ExitCode.RUNTIME_ERROR);
|
|
125
172
|
}).command("exec <path> [action]", "Execute operation on path", (yargs$1) => yargs$1.positional("path", {
|
|
@@ -134,14 +181,16 @@ async function main() {
|
|
|
134
181
|
type: "string",
|
|
135
182
|
description: "JSON parameters for the action"
|
|
136
183
|
}), async (argv) => {
|
|
184
|
+
const view = getViewType(argv);
|
|
185
|
+
await maybeShowHeader(view);
|
|
137
186
|
const params = argv.params ? JSON.parse(argv.params) : {};
|
|
138
187
|
const result = await execCommand(await createRuntime(), argv.path, argv.action, params);
|
|
139
|
-
const view = getViewType(argv);
|
|
140
188
|
console.log(formatExecOutput(result, view));
|
|
141
189
|
if (!result.success) process.exit(ExitCode.RUNTIME_ERROR);
|
|
142
190
|
}).command("mount", "Manage mount configurations", (yargs$1) => yargs$1.command(["list", "ls"], "List all mounts", () => {}, async (argv) => {
|
|
143
|
-
const result = await mountListCommand(process.cwd());
|
|
144
191
|
const view = getViewType(argv);
|
|
192
|
+
await maybeShowHeader(view);
|
|
193
|
+
const result = await mountListCommand(process.cwd());
|
|
145
194
|
console.log(formatMountListOutput(result.mounts, view));
|
|
146
195
|
}).command("add <path> <uri>", "Add a new mount (path=virtual path, uri=data source)", (yargs$2) => yargs$2.positional("path", {
|
|
147
196
|
type: "string",
|
|
@@ -155,11 +204,16 @@ async function main() {
|
|
|
155
204
|
type: "string",
|
|
156
205
|
description: "Human-readable description for this mount"
|
|
157
206
|
}), async (argv) => {
|
|
207
|
+
const view = getViewType(argv);
|
|
208
|
+
await maybeShowHeader(view);
|
|
158
209
|
const result = await mountAddCommand(process.cwd(), argv.path, argv.uri, { description: argv.description });
|
|
159
|
-
if (
|
|
160
|
-
else if (result.success)
|
|
161
|
-
|
|
162
|
-
console.
|
|
210
|
+
if (view === "json") console.log(JSON.stringify(result, null, 2));
|
|
211
|
+
else if (result.success) {
|
|
212
|
+
const msg = view === "human" ? `${colors.success("Added mount")} ${colors.cyan(result.normalizedPath)}` : `Added mount ${result.normalizedPath}`;
|
|
213
|
+
console.log(msg);
|
|
214
|
+
} else {
|
|
215
|
+
const msg = view === "human" ? colors.error(result.message) : result.message;
|
|
216
|
+
console.error(msg);
|
|
163
217
|
process.exit(ExitCode.RUNTIME_ERROR);
|
|
164
218
|
}
|
|
165
219
|
}).command(["remove <path>", "rm <path>"], "Remove a mount", (yargs$2) => yargs$2.positional("path", {
|
|
@@ -167,20 +221,33 @@ async function main() {
|
|
|
167
221
|
demandOption: true,
|
|
168
222
|
description: "Virtual path to remove (e.g., /src)"
|
|
169
223
|
}), async (argv) => {
|
|
224
|
+
const view = getViewType(argv);
|
|
225
|
+
await maybeShowHeader(view);
|
|
170
226
|
const result = await mountRemoveCommand(process.cwd(), argv.path);
|
|
171
|
-
if (
|
|
172
|
-
else if (result.success)
|
|
173
|
-
|
|
174
|
-
console.
|
|
227
|
+
if (view === "json") console.log(JSON.stringify(result, null, 2));
|
|
228
|
+
else if (result.success) {
|
|
229
|
+
const msg = view === "human" ? `${colors.success("Removed mount")} ${colors.cyan(argv.path)}` : `Removed mount ${argv.path}`;
|
|
230
|
+
console.log(msg);
|
|
231
|
+
} else {
|
|
232
|
+
const msg = view === "human" ? colors.error(result.message) : result.message;
|
|
233
|
+
console.error(msg);
|
|
175
234
|
process.exit(ExitCode.RUNTIME_ERROR);
|
|
176
235
|
}
|
|
177
236
|
}).command("validate", "Validate mount configuration", () => {}, async (argv) => {
|
|
237
|
+
const view = getViewType(argv);
|
|
238
|
+
await maybeShowHeader(view);
|
|
178
239
|
const result = await mountValidateCommand(process.cwd());
|
|
179
|
-
if (
|
|
180
|
-
else if (result.valid)
|
|
181
|
-
|
|
182
|
-
console.
|
|
183
|
-
|
|
240
|
+
if (view === "json") console.log(JSON.stringify(result, null, 2));
|
|
241
|
+
else if (result.valid) {
|
|
242
|
+
const msg = view === "human" ? colors.success("Configuration is valid") : "Configuration is valid";
|
|
243
|
+
console.log(msg);
|
|
244
|
+
} else {
|
|
245
|
+
const titleMsg = view === "human" ? colors.error("Configuration has errors:") : "Configuration has errors:";
|
|
246
|
+
console.error(titleMsg);
|
|
247
|
+
for (const error of result.errors) {
|
|
248
|
+
const errMsg = view === "human" ? ` ${colors.dim("-")} ${colors.error(error)}` : ` - ${error}`;
|
|
249
|
+
console.error(errMsg);
|
|
250
|
+
}
|
|
184
251
|
process.exit(ExitCode.RUNTIME_ERROR);
|
|
185
252
|
}
|
|
186
253
|
}).demandCommand(1, "Please specify a mount subcommand"), () => {}).command("explain [topic]", "Explain AFS concepts or AFS object", (yargs$1) => yargs$1.positional("topic", {
|
|
@@ -188,6 +255,7 @@ async function main() {
|
|
|
188
255
|
description: "Topic (mount, path, uri) or AFS path (e.g., /modules/src)"
|
|
189
256
|
}), async (argv) => {
|
|
190
257
|
const view = getViewType(argv);
|
|
258
|
+
await maybeShowHeader(view);
|
|
191
259
|
const topic = argv.topic;
|
|
192
260
|
if (topic?.startsWith("/")) {
|
|
193
261
|
const result = await explainPathCommand(await createRuntime(), topic);
|
|
@@ -220,6 +288,7 @@ async function main() {
|
|
|
220
288
|
type: "number",
|
|
221
289
|
description: "Maximum request body size in bytes (default: 10MB)"
|
|
222
290
|
}), async (argv) => {
|
|
291
|
+
await maybeShowHeader(getViewType(argv));
|
|
223
292
|
const result = await serveCommand({
|
|
224
293
|
host: argv.host,
|
|
225
294
|
port: argv.port,
|
|
@@ -230,6 +299,19 @@ async function main() {
|
|
|
230
299
|
});
|
|
231
300
|
console.log(formatServeOutput(result));
|
|
232
301
|
await new Promise(() => {});
|
|
302
|
+
}).command("explore [path]", "Interactive TUI file explorer (PC Tools style)", (yargs$1) => yargs$1.positional("path", {
|
|
303
|
+
type: "string",
|
|
304
|
+
default: "/",
|
|
305
|
+
description: "Starting path to explore"
|
|
306
|
+
}), async (argv) => {
|
|
307
|
+
const configLoader = new ConfigLoader();
|
|
308
|
+
const config = await configLoader.load(process.cwd());
|
|
309
|
+
await createExplorerScreen({
|
|
310
|
+
runtime: await createRuntime(process.cwd(), { configLoader }),
|
|
311
|
+
startPath: argv.path,
|
|
312
|
+
version: VERSION,
|
|
313
|
+
mountCount: config.mounts.length
|
|
314
|
+
});
|
|
233
315
|
}).demandCommand(1, "Please specify a command").strict();
|
|
234
316
|
try {
|
|
235
317
|
await cli.parse();
|
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.mjs","names":["yargs"],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * AFS CLI - Command Line Interface\n *\n * Commands:\n * - afs mount list|ls List mount configurations\n * - afs mount add <path> <uri> Add a mount\n * - afs mount remove|rm <path> Remove a mount\n * - afs mount validate Validate mount configuration\n * - afs list|ls [path] List directory\n * - afs stat <path> Get file/directory info\n * - afs read <path> Read file content\n * - afs write <path> Write file content (--content or stdin)\n * - afs exec <path> <action> Execute operation\n * - afs serve Start HTTP server to expose AFS\n *\n * Output modes:\n * - Default: Machine Truth (LLM/script friendly)\n * - --json: Structured JSON\n * - --view=llm: LLM optimized output\n * - --view=human: Human friendly format\n */\n\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport {\n execCommand,\n explainCommand,\n explainPathCommand,\n formatExecOutput,\n formatExplainOutput,\n formatLsOutput,\n formatMountListOutput,\n formatPathExplainOutput,\n formatReadOutput,\n formatServeOutput,\n formatStatOutput,\n formatWriteOutput,\n lsCommand,\n mountAddCommand,\n mountListCommand,\n mountRemoveCommand,\n mountValidateCommand,\n readCommand,\n serveCommand,\n statCommand,\n type ViewType,\n writeCommand,\n} from \"./commands/index.js\";\nimport { CLIError, ExitCode } from \"./errors.js\";\nimport { createRuntime } from \"./runtime.js\";\nimport { VERSION } from \"./version.js\";\n\n// Global view type derived from args\nfunction getViewType(argv: { json?: boolean; view?: string }): ViewType {\n if (argv.json) return \"json\";\n if (argv.view) return argv.view as ViewType;\n return \"default\";\n}\n\n// Run the CLI\nasync function main() {\n const cli = yargs(hideBin(process.argv))\n .scriptName(\"afs\")\n .version(VERSION)\n .alias(\"version\", \"V\")\n .help(\"help\")\n .alias(\"help\", \"h\")\n .usage(\"$0 <command> [options]\")\n .option(\"json\", {\n type: \"boolean\",\n description: \"Output in JSON format\",\n global: true,\n })\n .option(\"view\", {\n type: \"string\",\n choices: [\"default\", \"llm\", \"human\"],\n description: \"Output view format\",\n global: true,\n })\n .command(\n [\"list [path]\", \"ls [path]\"],\n \"List directory contents\",\n (yargs) =>\n yargs\n .positional(\"path\", {\n type: \"string\",\n default: \"/\",\n description: \"Path to list\",\n })\n .option(\"depth\", {\n type: \"number\",\n default: 1,\n description: \"Maximum depth to list\",\n })\n .option(\"limit\", {\n alias: \"n\",\n type: \"number\",\n description: \"Maximum number of entries to return\",\n })\n .option(\"max-children\", {\n type: \"number\",\n description: \"Maximum children per directory\",\n })\n .option(\"pattern\", {\n alias: \"p\",\n type: \"string\",\n description: \"Glob pattern to filter entries (e.g., *.ts, **/*.js)\",\n }),\n async (argv) => {\n const runtime = await createRuntime();\n const result = await lsCommand(runtime, argv.path!, {\n maxDepth: argv.depth,\n limit: argv.limit,\n maxChildren: argv[\"max-children\"],\n pattern: argv.pattern,\n });\n const view = getViewType(argv);\n console.log(formatLsOutput(result, view));\n },\n )\n .command(\n \"stat <path>\",\n \"Get file or directory info\",\n (yargs) =>\n yargs.positional(\"path\", {\n type: \"string\",\n demandOption: true,\n description: \"Path to stat\",\n }),\n async (argv) => {\n const runtime = await createRuntime();\n const result = await statCommand(runtime, argv.path!);\n const view = getViewType(argv);\n console.log(formatStatOutput(result, view));\n },\n )\n .command(\n \"read <path>\",\n \"Read file content\",\n (yargs) =>\n yargs.positional(\"path\", {\n type: \"string\",\n demandOption: true,\n description: \"Path to read\",\n }),\n async (argv) => {\n const runtime = await createRuntime();\n const result = await readCommand(runtime, argv.path!);\n const view = getViewType(argv);\n console.log(formatReadOutput(result, view));\n },\n )\n .command(\n \"write <path>\",\n \"Write content to file (from --content or stdin)\",\n (yargs) =>\n yargs\n .positional(\"path\", {\n type: \"string\",\n demandOption: true,\n description: \"Path to write\",\n })\n .option(\"content\", {\n type: \"string\",\n description: \"Content to write (if not provided, reads from stdin)\",\n })\n .option(\"append\", {\n type: \"boolean\",\n default: false,\n description: \"Append to file instead of overwrite\",\n }),\n async (argv) => {\n let content: string;\n\n if (argv.content !== undefined) {\n // Use --content parameter\n content = argv.content;\n } else {\n // Read content from stdin\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(chunk);\n }\n content = Buffer.concat(chunks).toString(\"utf-8\");\n }\n\n const runtime = await createRuntime();\n const result = await writeCommand(runtime, argv.path!, content, {\n append: argv.append,\n });\n const view = getViewType(argv);\n console.log(formatWriteOutput(result, view));\n\n if (!result.success) {\n process.exit(ExitCode.RUNTIME_ERROR);\n }\n },\n )\n .command(\n \"exec <path> [action]\",\n \"Execute operation on path\",\n (yargs) =>\n yargs\n .positional(\"path\", {\n type: \"string\",\n demandOption: true,\n description: \"Path to execute on\",\n })\n .positional(\"action\", {\n type: \"string\",\n default: \"default\",\n description: \"Action to execute\",\n })\n .option(\"params\", {\n type: \"string\",\n description: \"JSON parameters for the action\",\n }),\n async (argv) => {\n const params = argv.params ? JSON.parse(argv.params) : {};\n\n const runtime = await createRuntime();\n const result = await execCommand(runtime, argv.path!, argv.action!, params);\n const view = getViewType(argv);\n console.log(formatExecOutput(result, view));\n\n if (!result.success) {\n process.exit(ExitCode.RUNTIME_ERROR);\n }\n },\n )\n .command(\n \"mount\",\n \"Manage mount configurations\",\n (yargs) =>\n yargs\n .command(\n [\"list\", \"ls\"],\n \"List all mounts\",\n () => {},\n async (argv) => {\n const result = await mountListCommand(process.cwd());\n const view = getViewType(argv);\n console.log(formatMountListOutput(result.mounts, view));\n },\n )\n .command(\n \"add <path> <uri>\",\n \"Add a new mount (path=virtual path, uri=data source)\",\n (yargs) =>\n yargs\n .positional(\"path\", {\n type: \"string\",\n demandOption: true,\n description: \"Virtual path in AFS namespace (e.g., /src, /data)\",\n })\n .positional(\"uri\", {\n type: \"string\",\n demandOption: true,\n description: \"Data source URI: fs:///local/path, git://repo, sqlite:///db.sqlite\",\n })\n .option(\"description\", {\n type: \"string\",\n description: \"Human-readable description for this mount\",\n }),\n async (argv) => {\n const result = await mountAddCommand(process.cwd(), argv.path!, argv.uri!, {\n description: argv.description,\n });\n const view = getViewType(argv);\n\n if (view === \"json\") {\n console.log(JSON.stringify(result, null, 2));\n } else {\n if (result.success) {\n console.log(`Added mount ${argv.path}`);\n } else {\n console.error(result.message);\n process.exit(ExitCode.RUNTIME_ERROR);\n }\n }\n },\n )\n .command(\n [\"remove <path>\", \"rm <path>\"],\n \"Remove a mount\",\n (yargs) =>\n yargs.positional(\"path\", {\n type: \"string\",\n demandOption: true,\n description: \"Virtual path to remove (e.g., /src)\",\n }),\n async (argv) => {\n const result = await mountRemoveCommand(process.cwd(), argv.path!);\n const view = getViewType(argv);\n\n if (view === \"json\") {\n console.log(JSON.stringify(result, null, 2));\n } else {\n if (result.success) {\n console.log(`Removed mount ${argv.path}`);\n } else {\n console.error(result.message);\n process.exit(ExitCode.RUNTIME_ERROR);\n }\n }\n },\n )\n .command(\n \"validate\",\n \"Validate mount configuration\",\n () => {},\n async (argv) => {\n const result = await mountValidateCommand(process.cwd());\n const view = getViewType(argv);\n\n if (view === \"json\") {\n console.log(JSON.stringify(result, null, 2));\n } else {\n if (result.valid) {\n console.log(\"Configuration is valid\");\n } else {\n console.error(\"Configuration has errors:\");\n for (const error of result.errors) {\n console.error(` - ${error}`);\n }\n process.exit(ExitCode.RUNTIME_ERROR);\n }\n }\n },\n )\n .demandCommand(1, \"Please specify a mount subcommand\"),\n () => {},\n )\n .command(\n \"explain [topic]\",\n \"Explain AFS concepts or AFS object\",\n (yargs) =>\n yargs.positional(\"topic\", {\n type: \"string\",\n description: \"Topic (mount, path, uri) or AFS path (e.g., /modules/src)\",\n }),\n async (argv) => {\n const view = getViewType(argv);\n const topic = argv.topic;\n\n // If topic starts with /, treat as object path\n if (topic?.startsWith(\"/\")) {\n const runtime = await createRuntime();\n const result = await explainPathCommand(runtime, topic);\n console.log(formatPathExplainOutput(result, view));\n } else {\n const result = await explainCommand(process.cwd(), topic);\n console.log(formatExplainOutput(result, view));\n }\n },\n )\n .command(\n \"serve\",\n \"Start HTTP server to expose AFS providers\",\n (yargs) =>\n yargs\n .option(\"host\", {\n type: \"string\",\n default: \"localhost\",\n description: \"Host address to listen on\",\n })\n .option(\"port\", {\n type: \"number\",\n default: 3000,\n description: \"Port to listen on\",\n })\n .option(\"path\", {\n type: \"string\",\n default: \"/afs\",\n description: \"Base path for the server\",\n })\n .option(\"readonly\", {\n type: \"boolean\",\n default: false,\n description: \"Run in readonly mode (disable write operations)\",\n })\n .option(\"cors\", {\n type: \"boolean\",\n default: false,\n description: \"Enable CORS support\",\n })\n .option(\"max-body\", {\n type: \"number\",\n description: \"Maximum request body size in bytes (default: 10MB)\",\n }),\n async (argv) => {\n const result = await serveCommand({\n host: argv.host,\n port: argv.port,\n path: argv.path,\n readonly: argv.readonly,\n cors: argv.cors,\n maxBodySize: argv[\"max-body\"],\n });\n\n console.log(formatServeOutput(result));\n\n // Keep the process running\n await new Promise(() => {});\n },\n )\n .demandCommand(1, \"Please specify a command\")\n .strict();\n\n try {\n await cli.parse();\n } catch (error) {\n if (error instanceof CLIError) {\n console.error(error.message);\n process.exit(error.exitCode);\n }\n throw error;\n }\n}\n\nmain().catch((error) => {\n console.error(\"Fatal error:\", error.message);\n process.exit(ExitCode.RUNTIME_ERROR);\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":["yargs"],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * AFS CLI - Command Line Interface\n *\n * Commands:\n * - afs mount list|ls List mount configurations\n * - afs mount add <path> <uri> Add a mount\n * - afs mount remove|rm <path> Remove a mount\n * - afs mount validate Validate mount configuration\n * - afs list|ls [path] List directory\n * - afs stat <path> Get file/directory info\n * - afs read <path> Read file content\n * - afs write <path> Write file content (--content or stdin)\n * - afs exec <path> <action> Execute operation\n * - afs serve Start HTTP server to expose AFS\n * - afs explore [path] Interactive TUI file explorer\n *\n * Output modes (default: human):\n * - --view=human: Human friendly format with colors (default)\n * - --view=llm: LLM optimized output (token-efficient)\n * - --view=default: Machine truth (script/pipe friendly)\n * - --json: Structured JSON\n */\n\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport {\n execCommand,\n explainCommand,\n explainPathCommand,\n formatExecOutput,\n formatExplainOutput,\n formatLsOutput,\n formatMountListOutput,\n formatPathExplainOutput,\n formatReadOutput,\n formatServeOutput,\n formatStatOutput,\n formatWriteOutput,\n lsCommand,\n mountAddCommand,\n mountListCommand,\n mountRemoveCommand,\n mountValidateCommand,\n readCommand,\n serveCommand,\n statCommand,\n type ViewType,\n writeCommand,\n} from \"./commands/index.js\";\nimport { ConfigLoader } from \"./config/loader.js\";\nimport { CLIError, ExitCode } from \"./errors.js\";\nimport { createExplorerScreen } from \"./explorer/screen.js\";\nimport { createRuntime } from \"./runtime.js\";\nimport { colors, printHeader, shouldShowHeader } from \"./ui/index.js\";\nimport { VERSION } from \"./version.js\";\n\n// Global view type derived from args\n// Default is \"human\" for interactive use; LLMs can use --view=llm or --view=default\nfunction getViewType(argv: { json?: boolean; view?: string }): ViewType {\n if (argv.json) return \"json\";\n if (argv.view) return argv.view as ViewType;\n return \"human\";\n}\n\n// Track if header has been shown (show only once per invocation)\nlet headerShown = false;\n\n/**\n * Show header if in human view mode and not already shown\n */\nasync function maybeShowHeader(view: ViewType): Promise<void> {\n if (view !== \"human\" || headerShown || !shouldShowHeader()) {\n return;\n }\n\n headerShown = true;\n\n // Load mount count for header\n try {\n const configLoader = new ConfigLoader();\n const config = await configLoader.load(process.cwd());\n printHeader({\n version: VERSION,\n mountCount: config.mounts.length,\n });\n } catch {\n // If config loading fails, just show header without mount count\n printHeader({\n version: VERSION,\n mountCount: 0,\n });\n }\n}\n\n// Show header for help/version in interactive mode\nasync function showHeaderIfNeeded(): Promise<void> {\n const args = process.argv.slice(2);\n const isHelp =\n args.length === 0 ||\n args.includes(\"--help\") ||\n args.includes(\"-h\") ||\n (args.length === 1 && [\"help\", \"mount\", \"explain\"].includes(args[0]!));\n\n if (isHelp && shouldShowHeader()) {\n try {\n const configLoader = new ConfigLoader();\n const config = await configLoader.load(process.cwd());\n printHeader({\n version: VERSION,\n mountCount: config.mounts.length,\n });\n } catch {\n printHeader({\n version: VERSION,\n mountCount: 0,\n });\n }\n }\n}\n\n// Run the CLI\nasync function main() {\n await showHeaderIfNeeded();\n\n const cli = yargs(hideBin(process.argv))\n .scriptName(\"afs\")\n .version(VERSION)\n .alias(\"version\", \"V\")\n .help(\"help\")\n .alias(\"help\", \"h\")\n .usage(\"$0 <command> [options]\")\n .option(\"json\", {\n type: \"boolean\",\n description: \"Output in JSON format\",\n global: true,\n })\n .option(\"view\", {\n type: \"string\",\n choices: [\"default\", \"llm\", \"human\"],\n default: \"human\",\n description: \"Output format (llm for AI agents, default for scripts)\",\n global: true,\n })\n .command(\n [\"list [path]\", \"ls [path]\"],\n \"List directory contents\",\n (yargs) =>\n yargs\n .positional(\"path\", {\n type: \"string\",\n default: \"/\",\n description: \"Path to list\",\n })\n .option(\"depth\", {\n type: \"number\",\n default: 1,\n description: \"Maximum depth to list\",\n })\n .option(\"limit\", {\n alias: \"n\",\n type: \"number\",\n description: \"Maximum number of entries to return\",\n })\n .option(\"max-children\", {\n type: \"number\",\n description: \"Maximum children per directory\",\n })\n .option(\"pattern\", {\n alias: \"p\",\n type: \"string\",\n description: \"Glob pattern to filter entries (e.g., *.ts, **/*.js)\",\n }),\n async (argv) => {\n const view = getViewType(argv);\n await maybeShowHeader(view);\n const runtime = await createRuntime();\n const result = await lsCommand(runtime, argv.path!, {\n maxDepth: argv.depth,\n limit: argv.limit,\n maxChildren: argv[\"max-children\"],\n pattern: argv.pattern,\n });\n console.log(formatLsOutput(result, view));\n },\n )\n .command(\n \"stat <path>\",\n \"Get file or directory info\",\n (yargs) =>\n yargs.positional(\"path\", {\n type: \"string\",\n demandOption: true,\n description: \"Path to stat\",\n }),\n async (argv) => {\n const view = getViewType(argv);\n await maybeShowHeader(view);\n const runtime = await createRuntime();\n const result = await statCommand(runtime, argv.path!);\n console.log(formatStatOutput(result, view));\n },\n )\n .command(\n \"read <path>\",\n \"Read file content\",\n (yargs) =>\n yargs.positional(\"path\", {\n type: \"string\",\n demandOption: true,\n description: \"Path to read\",\n }),\n async (argv) => {\n const view = getViewType(argv);\n await maybeShowHeader(view);\n const runtime = await createRuntime();\n const result = await readCommand(runtime, argv.path!);\n console.log(formatReadOutput(result, view));\n },\n )\n .command(\n \"write <path>\",\n \"Write content to file (from --content or stdin)\",\n (yargs) =>\n yargs\n .positional(\"path\", {\n type: \"string\",\n demandOption: true,\n description: \"Path to write\",\n })\n .option(\"content\", {\n type: \"string\",\n description: \"Content to write (if not provided, reads from stdin)\",\n })\n .option(\"append\", {\n type: \"boolean\",\n default: false,\n description: \"Append to file instead of overwrite\",\n }),\n async (argv) => {\n const view = getViewType(argv);\n await maybeShowHeader(view);\n\n let content: string;\n\n if (argv.content !== undefined) {\n // Use --content parameter\n content = argv.content;\n } else {\n // Read content from stdin\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(chunk);\n }\n content = Buffer.concat(chunks).toString(\"utf-8\");\n }\n\n const runtime = await createRuntime();\n const result = await writeCommand(runtime, argv.path!, content, {\n append: argv.append,\n });\n console.log(formatWriteOutput(result, view));\n\n if (!result.success) {\n process.exit(ExitCode.RUNTIME_ERROR);\n }\n },\n )\n .command(\n \"exec <path> [action]\",\n \"Execute operation on path\",\n (yargs) =>\n yargs\n .positional(\"path\", {\n type: \"string\",\n demandOption: true,\n description: \"Path to execute on\",\n })\n .positional(\"action\", {\n type: \"string\",\n default: \"default\",\n description: \"Action to execute\",\n })\n .option(\"params\", {\n type: \"string\",\n description: \"JSON parameters for the action\",\n }),\n async (argv) => {\n const view = getViewType(argv);\n await maybeShowHeader(view);\n\n const params = argv.params ? JSON.parse(argv.params) : {};\n\n const runtime = await createRuntime();\n const result = await execCommand(runtime, argv.path!, argv.action!, params);\n console.log(formatExecOutput(result, view));\n\n if (!result.success) {\n process.exit(ExitCode.RUNTIME_ERROR);\n }\n },\n )\n .command(\n \"mount\",\n \"Manage mount configurations\",\n (yargs) =>\n yargs\n .command(\n [\"list\", \"ls\"],\n \"List all mounts\",\n () => {},\n async (argv) => {\n const view = getViewType(argv);\n await maybeShowHeader(view);\n const result = await mountListCommand(process.cwd());\n console.log(formatMountListOutput(result.mounts, view));\n },\n )\n .command(\n \"add <path> <uri>\",\n \"Add a new mount (path=virtual path, uri=data source)\",\n (yargs) =>\n yargs\n .positional(\"path\", {\n type: \"string\",\n demandOption: true,\n description: \"Virtual path in AFS namespace (e.g., /src, /data)\",\n })\n .positional(\"uri\", {\n type: \"string\",\n demandOption: true,\n description: \"Data source URI: fs:///local/path, git://repo, sqlite:///db.sqlite\",\n })\n .option(\"description\", {\n type: \"string\",\n description: \"Human-readable description for this mount\",\n }),\n async (argv) => {\n const view = getViewType(argv);\n await maybeShowHeader(view);\n\n const result = await mountAddCommand(process.cwd(), argv.path!, argv.uri!, {\n description: argv.description,\n });\n\n if (view === \"json\") {\n console.log(JSON.stringify(result, null, 2));\n } else {\n if (result.success) {\n const msg =\n view === \"human\"\n ? `${colors.success(\"Added mount\")} ${colors.cyan(result.normalizedPath!)}`\n : `Added mount ${result.normalizedPath}`;\n console.log(msg);\n } else {\n const msg = view === \"human\" ? colors.error(result.message!) : result.message;\n console.error(msg);\n process.exit(ExitCode.RUNTIME_ERROR);\n }\n }\n },\n )\n .command(\n [\"remove <path>\", \"rm <path>\"],\n \"Remove a mount\",\n (yargs) =>\n yargs.positional(\"path\", {\n type: \"string\",\n demandOption: true,\n description: \"Virtual path to remove (e.g., /src)\",\n }),\n async (argv) => {\n const view = getViewType(argv);\n await maybeShowHeader(view);\n\n const result = await mountRemoveCommand(process.cwd(), argv.path!);\n\n if (view === \"json\") {\n console.log(JSON.stringify(result, null, 2));\n } else {\n if (result.success) {\n const msg =\n view === \"human\"\n ? `${colors.success(\"Removed mount\")} ${colors.cyan(argv.path!)}`\n : `Removed mount ${argv.path}`;\n console.log(msg);\n } else {\n const msg = view === \"human\" ? colors.error(result.message!) : result.message;\n console.error(msg);\n process.exit(ExitCode.RUNTIME_ERROR);\n }\n }\n },\n )\n .command(\n \"validate\",\n \"Validate mount configuration\",\n () => {},\n async (argv) => {\n const view = getViewType(argv);\n await maybeShowHeader(view);\n\n const result = await mountValidateCommand(process.cwd());\n\n if (view === \"json\") {\n console.log(JSON.stringify(result, null, 2));\n } else {\n if (result.valid) {\n const msg =\n view === \"human\"\n ? colors.success(\"Configuration is valid\")\n : \"Configuration is valid\";\n console.log(msg);\n } else {\n const titleMsg =\n view === \"human\"\n ? colors.error(\"Configuration has errors:\")\n : \"Configuration has errors:\";\n console.error(titleMsg);\n for (const error of result.errors) {\n const errMsg =\n view === \"human\"\n ? ` ${colors.dim(\"-\")} ${colors.error(error)}`\n : ` - ${error}`;\n console.error(errMsg);\n }\n process.exit(ExitCode.RUNTIME_ERROR);\n }\n }\n },\n )\n .demandCommand(1, \"Please specify a mount subcommand\"),\n () => {},\n )\n .command(\n \"explain [topic]\",\n \"Explain AFS concepts or AFS object\",\n (yargs) =>\n yargs.positional(\"topic\", {\n type: \"string\",\n description: \"Topic (mount, path, uri) or AFS path (e.g., /modules/src)\",\n }),\n async (argv) => {\n const view = getViewType(argv);\n await maybeShowHeader(view);\n const topic = argv.topic;\n\n // If topic starts with /, treat as object path\n if (topic?.startsWith(\"/\")) {\n const runtime = await createRuntime();\n const result = await explainPathCommand(runtime, topic);\n console.log(formatPathExplainOutput(result, view));\n } else {\n const result = await explainCommand(process.cwd(), topic);\n console.log(formatExplainOutput(result, view));\n }\n },\n )\n .command(\n \"serve\",\n \"Start HTTP server to expose AFS providers\",\n (yargs) =>\n yargs\n .option(\"host\", {\n type: \"string\",\n default: \"localhost\",\n description: \"Host address to listen on\",\n })\n .option(\"port\", {\n type: \"number\",\n default: 3000,\n description: \"Port to listen on\",\n })\n .option(\"path\", {\n type: \"string\",\n default: \"/afs\",\n description: \"Base path for the server\",\n })\n .option(\"readonly\", {\n type: \"boolean\",\n default: false,\n description: \"Run in readonly mode (disable write operations)\",\n })\n .option(\"cors\", {\n type: \"boolean\",\n default: false,\n description: \"Enable CORS support\",\n })\n .option(\"max-body\", {\n type: \"number\",\n description: \"Maximum request body size in bytes (default: 10MB)\",\n }),\n async (argv) => {\n const view = getViewType(argv);\n await maybeShowHeader(view);\n\n const result = await serveCommand({\n host: argv.host,\n port: argv.port,\n path: argv.path,\n readonly: argv.readonly,\n cors: argv.cors,\n maxBodySize: argv[\"max-body\"],\n });\n\n console.log(formatServeOutput(result));\n\n // Keep the process running\n await new Promise(() => {});\n },\n )\n .command(\n \"explore [path]\",\n \"Interactive TUI file explorer (PC Tools style)\",\n (yargs) =>\n yargs.positional(\"path\", {\n type: \"string\",\n default: \"/\",\n description: \"Starting path to explore\",\n }),\n async (argv) => {\n const configLoader = new ConfigLoader();\n const config = await configLoader.load(process.cwd());\n const runtime = await createRuntime(process.cwd(), { configLoader });\n await createExplorerScreen({\n runtime,\n startPath: argv.path,\n version: VERSION,\n mountCount: config.mounts.length,\n });\n },\n )\n .demandCommand(1, \"Please specify a command\")\n .strict();\n\n try {\n await cli.parse();\n } catch (error) {\n if (error instanceof CLIError) {\n console.error(error.message);\n process.exit(error.exitCode);\n }\n throw error;\n }\n}\n\nmain().catch((error) => {\n console.error(\"Fatal error:\", error.message);\n process.exit(ExitCode.RUNTIME_ERROR);\n});\n"],"mappings":"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"}
|
package/dist/commands/ls.cjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const require_index = require('../ui/index.cjs');
|
|
1
2
|
|
|
2
3
|
//#region src/commands/ls.ts
|
|
3
4
|
/**
|
|
@@ -107,7 +108,7 @@ function formatHuman(result) {
|
|
|
107
108
|
renderTree(root, "", lines);
|
|
108
109
|
if (result.truncated) {
|
|
109
110
|
lines.push("");
|
|
110
|
-
lines.push(`(Results truncated - ${result.total} entries shown)`);
|
|
111
|
+
lines.push(require_index.colors.yellow(`(Results truncated - ${result.total} entries shown)`));
|
|
111
112
|
}
|
|
112
113
|
return lines.join("\n");
|
|
113
114
|
}
|
|
@@ -116,10 +117,11 @@ function renderTree(node, prefix, lines) {
|
|
|
116
117
|
for (let i = 0; i < children.length; i++) {
|
|
117
118
|
const child = children[i];
|
|
118
119
|
const isLast = i === children.length - 1;
|
|
119
|
-
const connector = isLast ? "└── " : "├── ";
|
|
120
|
+
const connector = require_index.colors.dim(isLast ? "└── " : "├── ");
|
|
120
121
|
const icon = child.entry ? getTypeIcon(child.entry.type) : "📂";
|
|
121
|
-
const
|
|
122
|
-
|
|
122
|
+
const name = child.entry?.type === "directory" ? require_index.colors.cyan(child.name) : child.name;
|
|
123
|
+
const sizeStr = child.entry?.size !== void 0 ? require_index.colors.dim(` ${formatSize(child.entry.size)}`) : "";
|
|
124
|
+
lines.push(`${require_index.colors.dim(prefix)}${connector}${icon} ${name}${sizeStr}`);
|
|
123
125
|
renderTree(child, prefix + (isLast ? " " : "│ "), lines);
|
|
124
126
|
}
|
|
125
127
|
}
|
package/dist/commands/ls.mjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { colors } from "../ui/index.mjs";
|
|
2
|
+
|
|
1
3
|
//#region src/commands/ls.ts
|
|
2
4
|
/**
|
|
3
5
|
* List directory contents
|
|
@@ -106,7 +108,7 @@ function formatHuman(result) {
|
|
|
106
108
|
renderTree(root, "", lines);
|
|
107
109
|
if (result.truncated) {
|
|
108
110
|
lines.push("");
|
|
109
|
-
lines.push(`(Results truncated - ${result.total} entries shown)`);
|
|
111
|
+
lines.push(colors.yellow(`(Results truncated - ${result.total} entries shown)`));
|
|
110
112
|
}
|
|
111
113
|
return lines.join("\n");
|
|
112
114
|
}
|
|
@@ -115,10 +117,11 @@ function renderTree(node, prefix, lines) {
|
|
|
115
117
|
for (let i = 0; i < children.length; i++) {
|
|
116
118
|
const child = children[i];
|
|
117
119
|
const isLast = i === children.length - 1;
|
|
118
|
-
const connector = isLast ? "└── " : "├── ";
|
|
120
|
+
const connector = colors.dim(isLast ? "└── " : "├── ");
|
|
119
121
|
const icon = child.entry ? getTypeIcon(child.entry.type) : "📂";
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
+
const name = child.entry?.type === "directory" ? colors.cyan(child.name) : child.name;
|
|
123
|
+
const sizeStr = child.entry?.size !== void 0 ? colors.dim(` ${formatSize(child.entry.size)}`) : "";
|
|
124
|
+
lines.push(`${colors.dim(prefix)}${connector}${icon} ${name}${sizeStr}`);
|
|
122
125
|
renderTree(child, prefix + (isLast ? " " : "│ "), lines);
|
|
123
126
|
}
|
|
124
127
|
}
|
package/dist/commands/ls.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ls.mjs","names":[],"sources":["../../src/commands/ls.ts"],"sourcesContent":["import type { AFSEntry } from \"@aigne/afs\";\nimport type { AFSRuntime } from \"../runtime.js\";\n\nexport type ViewType = \"default\" | \"json\" | \"llm\" | \"human\";\n\nexport interface LsEntry {\n path: string;\n type: \"file\" | \"directory\";\n size?: number;\n modified?: string;\n hash?: string;\n childrenCount?: number;\n childrenTruncated?: boolean;\n}\n\nexport interface LsOptions {\n maxDepth?: number;\n limit?: number;\n maxChildren?: number;\n pattern?: string;\n}\n\nexport interface LsResult {\n entries: LsEntry[];\n total: number;\n truncated?: boolean;\n message?: string;\n}\n\n/**\n * List directory contents\n */\nexport async function lsCommand(\n runtime: AFSRuntime,\n path: string,\n options: LsOptions = {},\n): Promise<LsResult> {\n const result = await runtime.list(path, {\n maxDepth: options.maxDepth ?? 1,\n limit: options.limit,\n maxChildren: options.maxChildren,\n pattern: options.pattern,\n });\n\n const entries: LsEntry[] = result.data.map((entry: AFSEntry) => ({\n path: entry.path,\n type: mapEntryType(entry.metadata?.type),\n size: entry.metadata?.size,\n modified: formatDate(entry.updatedAt),\n childrenCount: entry.metadata?.childrenCount,\n childrenTruncated: entry.metadata?.childrenTruncated,\n }));\n\n // Check if any entry has truncated children\n const hasTruncatedChildren = entries.some((e) => e.childrenTruncated);\n\n // Determine if results were truncated\n const truncated =\n hasTruncatedChildren ||\n (options.limit !== undefined && entries.length >= options.limit) ||\n result.message?.toLowerCase().includes(\"truncat\");\n\n return {\n entries,\n total: entries.length,\n truncated,\n message: result.message,\n };\n}\n\nfunction mapEntryType(type?: string): LsEntry[\"type\"] {\n // Only \"file\" is a true file, all other types are treated as directories\n if (type === \"file\") {\n return \"file\";\n }\n return \"directory\";\n}\n\nfunction formatDate(date: Date | string | undefined): string | undefined {\n if (!date) return undefined;\n // If it's already a string (from HTTP JSON), return as-is\n if (typeof date === \"string\") return date;\n // If it's a Date object, convert to ISO string\n return date.toISOString();\n}\n\n/**\n * Format ls output for different views\n */\nexport function formatLsOutput(result: LsResult, view: ViewType): string {\n switch (view) {\n case \"json\":\n return formatJson(result);\n case \"llm\":\n return formatLlm(result);\n case \"human\":\n return formatHuman(result);\n default:\n return formatDefault(result);\n }\n}\n\n/**\n * Default format: Machine truth, one path per line\n */\nfunction formatDefault(result: LsResult): string {\n const lines = result.entries.map((entry) => entry.path);\n if (result.truncated) {\n lines.push(`# Results truncated (${result.total} shown)`);\n }\n return lines.join(\"\\n\");\n}\n\n/**\n * JSON format: Structured output\n */\nfunction formatJson(result: LsResult): string {\n return JSON.stringify(\n {\n entries: result.entries,\n total: result.total,\n truncated: result.truncated,\n message: result.message,\n },\n null,\n 2,\n );\n}\n\n/**\n * LLM format: Token-efficient, semantic facts\n */\nfunction formatLlm(result: LsResult): string {\n const lines: string[] = [];\n\n for (const entry of result.entries) {\n const parts = [`ENTRY ${entry.path}`];\n parts.push(`TYPE=${entry.type}`);\n\n if (entry.size !== undefined) {\n parts.push(`SIZE=${entry.size}`);\n }\n\n if (entry.childrenCount !== undefined) {\n parts.push(`CHILDREN=${entry.childrenCount}`);\n }\n\n if (entry.childrenTruncated) {\n parts.push(\"TRUNCATED\");\n }\n\n lines.push(parts.join(\" \"));\n }\n\n lines.push(`TOTAL ${result.total}`);\n if (result.truncated) {\n lines.push(\"TRUNCATED true\");\n }\n return lines.join(\"\\n\");\n}\n\ninterface TreeNode {\n name: string;\n entry?: LsEntry;\n children: Map<string, TreeNode>;\n}\n\n/**\n * Human format: Tree structure\n */\nfunction formatHuman(result: LsResult): string {\n // Build tree structure from flat paths\n const root: TreeNode = { name: \"\", children: new Map() };\n\n for (const entry of result.entries) {\n const parts = entry.path.split(\"/\").filter(Boolean);\n let current = root;\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i]!;\n if (!current.children.has(part)) {\n current.children.set(part, { name: part, children: new Map() });\n }\n current = current.children.get(part)!;\n\n // Attach entry to the leaf node\n if (i === parts.length - 1) {\n current.entry = entry;\n }\n }\n }\n\n // Render tree\n const lines: string[] = [];\n renderTree(root, \"\", lines);\n\n if (result.truncated) {\n lines.push(\"\");\n lines.push(`(Results truncated - ${result.total} entries shown)`);\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction renderTree(node: TreeNode, prefix: string, lines: string[]): void {\n const children = Array.from(node.children.values());\n\n for (let i = 0; i < children.length; i++) {\n const child = children[i]!;\n const isLast = i === children.length - 1;\n const connector = isLast ? \"└── \" : \"├── \";\n const icon = child.entry ? getTypeIcon(child.entry.type) : \"\\u{1F4C2}\"; // 📂\n const
|
|
1
|
+
{"version":3,"file":"ls.mjs","names":[],"sources":["../../src/commands/ls.ts"],"sourcesContent":["import type { AFSEntry } from \"@aigne/afs\";\nimport type { AFSRuntime } from \"../runtime.js\";\nimport { colors } from \"../ui/index.js\";\n\nexport type ViewType = \"default\" | \"json\" | \"llm\" | \"human\";\n\nexport interface LsEntry {\n path: string;\n type: \"file\" | \"directory\";\n size?: number;\n modified?: string;\n hash?: string;\n childrenCount?: number;\n childrenTruncated?: boolean;\n}\n\nexport interface LsOptions {\n maxDepth?: number;\n limit?: number;\n maxChildren?: number;\n pattern?: string;\n}\n\nexport interface LsResult {\n entries: LsEntry[];\n total: number;\n truncated?: boolean;\n message?: string;\n}\n\n/**\n * List directory contents\n */\nexport async function lsCommand(\n runtime: AFSRuntime,\n path: string,\n options: LsOptions = {},\n): Promise<LsResult> {\n const result = await runtime.list(path, {\n maxDepth: options.maxDepth ?? 1,\n limit: options.limit,\n maxChildren: options.maxChildren,\n pattern: options.pattern,\n });\n\n const entries: LsEntry[] = result.data.map((entry: AFSEntry) => ({\n path: entry.path,\n type: mapEntryType(entry.metadata?.type),\n size: entry.metadata?.size,\n modified: formatDate(entry.updatedAt),\n childrenCount: entry.metadata?.childrenCount,\n childrenTruncated: entry.metadata?.childrenTruncated,\n }));\n\n // Check if any entry has truncated children\n const hasTruncatedChildren = entries.some((e) => e.childrenTruncated);\n\n // Determine if results were truncated\n const truncated =\n hasTruncatedChildren ||\n (options.limit !== undefined && entries.length >= options.limit) ||\n result.message?.toLowerCase().includes(\"truncat\");\n\n return {\n entries,\n total: entries.length,\n truncated,\n message: result.message,\n };\n}\n\nfunction mapEntryType(type?: string): LsEntry[\"type\"] {\n // Only \"file\" is a true file, all other types are treated as directories\n if (type === \"file\") {\n return \"file\";\n }\n return \"directory\";\n}\n\nfunction formatDate(date: Date | string | undefined): string | undefined {\n if (!date) return undefined;\n // If it's already a string (from HTTP JSON), return as-is\n if (typeof date === \"string\") return date;\n // If it's a Date object, convert to ISO string\n return date.toISOString();\n}\n\n/**\n * Format ls output for different views\n */\nexport function formatLsOutput(result: LsResult, view: ViewType): string {\n switch (view) {\n case \"json\":\n return formatJson(result);\n case \"llm\":\n return formatLlm(result);\n case \"human\":\n return formatHuman(result);\n default:\n return formatDefault(result);\n }\n}\n\n/**\n * Default format: Machine truth, one path per line\n */\nfunction formatDefault(result: LsResult): string {\n const lines = result.entries.map((entry) => entry.path);\n if (result.truncated) {\n lines.push(`# Results truncated (${result.total} shown)`);\n }\n return lines.join(\"\\n\");\n}\n\n/**\n * JSON format: Structured output\n */\nfunction formatJson(result: LsResult): string {\n return JSON.stringify(\n {\n entries: result.entries,\n total: result.total,\n truncated: result.truncated,\n message: result.message,\n },\n null,\n 2,\n );\n}\n\n/**\n * LLM format: Token-efficient, semantic facts\n */\nfunction formatLlm(result: LsResult): string {\n const lines: string[] = [];\n\n for (const entry of result.entries) {\n const parts = [`ENTRY ${entry.path}`];\n parts.push(`TYPE=${entry.type}`);\n\n if (entry.size !== undefined) {\n parts.push(`SIZE=${entry.size}`);\n }\n\n if (entry.childrenCount !== undefined) {\n parts.push(`CHILDREN=${entry.childrenCount}`);\n }\n\n if (entry.childrenTruncated) {\n parts.push(\"TRUNCATED\");\n }\n\n lines.push(parts.join(\" \"));\n }\n\n lines.push(`TOTAL ${result.total}`);\n if (result.truncated) {\n lines.push(\"TRUNCATED true\");\n }\n return lines.join(\"\\n\");\n}\n\ninterface TreeNode {\n name: string;\n entry?: LsEntry;\n children: Map<string, TreeNode>;\n}\n\n/**\n * Human format: Tree structure\n */\nfunction formatHuman(result: LsResult): string {\n // Build tree structure from flat paths\n const root: TreeNode = { name: \"\", children: new Map() };\n\n for (const entry of result.entries) {\n const parts = entry.path.split(\"/\").filter(Boolean);\n let current = root;\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i]!;\n if (!current.children.has(part)) {\n current.children.set(part, { name: part, children: new Map() });\n }\n current = current.children.get(part)!;\n\n // Attach entry to the leaf node\n if (i === parts.length - 1) {\n current.entry = entry;\n }\n }\n }\n\n // Render tree\n const lines: string[] = [];\n renderTree(root, \"\", lines);\n\n if (result.truncated) {\n lines.push(\"\");\n lines.push(colors.yellow(`(Results truncated - ${result.total} entries shown)`));\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction renderTree(node: TreeNode, prefix: string, lines: string[]): void {\n const children = Array.from(node.children.values());\n\n for (let i = 0; i < children.length; i++) {\n const child = children[i]!;\n const isLast = i === children.length - 1;\n const connector = colors.dim(isLast ? \"└── \" : \"├── \");\n const icon = child.entry ? getTypeIcon(child.entry.type) : \"\\u{1F4C2}\"; // 📂\n const name = child.entry?.type === \"directory\" ? colors.cyan(child.name) : child.name;\n const sizeStr =\n child.entry?.size !== undefined ? colors.dim(` ${formatSize(child.entry.size)}`) : \"\";\n\n lines.push(`${colors.dim(prefix)}${connector}${icon} ${name}${sizeStr}`);\n\n // Recurse into children\n const childPrefix = prefix + (isLast ? \" \" : \"│ \");\n renderTree(child, childPrefix, lines);\n }\n}\n\nfunction getTypeIcon(type: LsEntry[\"type\"]): string {\n switch (type) {\n case \"directory\":\n return \"\\u{1F4C2}\"; // 📂\n case \"file\":\n return \"\\u{1F4C4}\"; // 📄\n default:\n return \"\\u{1F4C4}\"; // 📄\n }\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n}\n"],"mappings":"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"}
|
package/dist/commands/mount.cjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
|
|
2
|
+
const require_schema = require('../config/schema.cjs');
|
|
2
3
|
const require_loader = require('../config/loader.cjs');
|
|
4
|
+
const require_index = require('../ui/index.cjs');
|
|
3
5
|
let node_fs_promises = require("node:fs/promises");
|
|
4
6
|
let node_path = require("node:path");
|
|
5
7
|
let smol_toml = require("smol-toml");
|
|
@@ -47,27 +49,45 @@ async function mountListCommand(cwd) {
|
|
|
47
49
|
* Add a mount to config
|
|
48
50
|
*/
|
|
49
51
|
async function mountAddCommand(cwd, path, uri, options = {}) {
|
|
52
|
+
if (!uri || uri.trim() === "") return {
|
|
53
|
+
success: false,
|
|
54
|
+
message: "URI is required"
|
|
55
|
+
};
|
|
56
|
+
const resolvedUri = resolveUriPath(uri, cwd);
|
|
57
|
+
const validation = require_schema.MountSchema.safeParse({
|
|
58
|
+
path,
|
|
59
|
+
uri: resolvedUri,
|
|
60
|
+
...options
|
|
61
|
+
});
|
|
62
|
+
if (!validation.success) return {
|
|
63
|
+
success: false,
|
|
64
|
+
message: validation.error.errors.map((e) => e.message).join("; ")
|
|
65
|
+
};
|
|
66
|
+
const newMount = {
|
|
67
|
+
path: validation.data.path,
|
|
68
|
+
uri: validation.data.uri,
|
|
69
|
+
...options.description && { description: options.description }
|
|
70
|
+
};
|
|
50
71
|
const configDir = (0, node_path.join)(cwd, require_loader.CONFIG_DIR_NAME);
|
|
51
72
|
const configPath = (0, node_path.join)(configDir, require_loader.CONFIG_FILE_NAME);
|
|
52
73
|
const config = { mounts: [] };
|
|
53
74
|
try {
|
|
54
75
|
config.mounts = (0, smol_toml.parse)(await (0, node_fs_promises.readFile)(configPath, "utf-8")).mounts ?? [];
|
|
55
76
|
} catch {}
|
|
56
|
-
|
|
77
|
+
const normalizedPath = validation.data.path;
|
|
78
|
+
if (config.mounts.some((m) => m.path === normalizedPath)) return {
|
|
57
79
|
success: false,
|
|
58
|
-
message: `Mount path "${
|
|
59
|
-
};
|
|
60
|
-
const newMount = {
|
|
61
|
-
path,
|
|
62
|
-
uri: resolveUriPath(uri, cwd),
|
|
63
|
-
...options.description && { description: options.description }
|
|
80
|
+
message: `Mount path "${normalizedPath}" already exists`
|
|
64
81
|
};
|
|
65
82
|
config.mounts.push(newMount);
|
|
66
83
|
try {
|
|
67
84
|
await (0, node_fs_promises.mkdir)(configDir, { recursive: true });
|
|
68
85
|
} catch {}
|
|
69
86
|
await (0, node_fs_promises.writeFile)(configPath, (0, smol_toml.stringify)(config), "utf-8");
|
|
70
|
-
return {
|
|
87
|
+
return {
|
|
88
|
+
success: true,
|
|
89
|
+
normalizedPath
|
|
90
|
+
};
|
|
71
91
|
}
|
|
72
92
|
/**
|
|
73
93
|
* Remove a mount from config
|
|
@@ -102,8 +122,11 @@ async function mountValidateCommand(cwd) {
|
|
|
102
122
|
try {
|
|
103
123
|
const mounts = (0, smol_toml.parse)(await (0, node_fs_promises.readFile)(configPath, "utf-8")).mounts ?? [];
|
|
104
124
|
for (const mount of mounts) {
|
|
105
|
-
|
|
106
|
-
if (!
|
|
125
|
+
const validation = require_schema.MountSchema.safeParse(mount);
|
|
126
|
+
if (!validation.success) {
|
|
127
|
+
for (const err of validation.error.errors) errors.push(`Mount "${mount.path}": ${err.message}`);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
107
130
|
if (mount.uri.startsWith("fs://")) {
|
|
108
131
|
const targetPath = mount.uri.replace("fs://", "");
|
|
109
132
|
try {
|
|
@@ -151,12 +174,12 @@ function formatLlm(mounts) {
|
|
|
151
174
|
}).join("\n\n");
|
|
152
175
|
}
|
|
153
176
|
function formatHuman(mounts) {
|
|
154
|
-
if (mounts.length === 0) return "No mounts configured.";
|
|
155
|
-
const lines = ["Configured Mounts:", ""];
|
|
177
|
+
if (mounts.length === 0) return require_index.colors.dim("No mounts configured.");
|
|
178
|
+
const lines = [require_index.colors.bold("Configured Mounts:"), ""];
|
|
156
179
|
for (const m of mounts) {
|
|
157
|
-
lines.push(` ${m.path}`);
|
|
158
|
-
lines.push(` URI: ${m.uri}`);
|
|
159
|
-
if (m.description) lines.push(` Description: ${m.description}`);
|
|
180
|
+
lines.push(` ${require_index.colors.cyan(m.path)}`);
|
|
181
|
+
lines.push(` ${require_index.colors.dim("URI:")} ${m.uri}`);
|
|
182
|
+
if (m.description) lines.push(` ${require_index.colors.dim("Description:")} ${m.description}`);
|
|
160
183
|
lines.push("");
|
|
161
184
|
}
|
|
162
185
|
return lines.join("\n").trimEnd();
|