@aigne/afs-cli 1.11.0-beta.4 → 1.11.0-beta.6
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 +16 -1
- package/dist/cli.cjs +53 -20
- package/dist/cli.mjs +54 -21
- package/dist/cli.mjs.map +1 -1
- package/dist/commands/exec.cjs +132 -14
- package/dist/commands/exec.mjs +129 -14
- package/dist/commands/exec.mjs.map +1 -1
- package/dist/commands/explain.cjs +1 -1
- package/dist/commands/explain.mjs +1 -1
- package/dist/commands/explain.mjs.map +1 -1
- package/dist/commands/index.mjs +1 -1
- package/dist/commands/ls.cjs +129 -30
- package/dist/commands/ls.mjs +129 -30
- package/dist/commands/ls.mjs.map +1 -1
- package/dist/commands/mount.cjs +2 -1
- package/dist/commands/mount.mjs +2 -1
- package/dist/commands/mount.mjs.map +1 -1
- package/dist/commands/read.cjs +213 -14
- package/dist/commands/read.mjs +213 -14
- package/dist/commands/read.mjs.map +1 -1
- package/dist/commands/serve.cjs +3 -1
- package/dist/commands/serve.mjs +3 -1
- package/dist/commands/serve.mjs.map +1 -1
- package/dist/commands/stat.cjs +116 -34
- package/dist/commands/stat.mjs +117 -34
- package/dist/commands/stat.mjs.map +1 -1
- package/dist/commands/write.cjs +37 -4
- package/dist/commands/write.mjs +38 -4
- package/dist/commands/write.mjs.map +1 -1
- package/dist/config/loader.cjs +33 -13
- package/dist/config/loader.mjs +33 -13
- package/dist/config/loader.mjs.map +1 -1
- package/dist/config/provider-factory.cjs +311 -3
- package/dist/config/provider-factory.mjs +311 -3
- package/dist/config/provider-factory.mjs.map +1 -1
- package/dist/config/schema.cjs +3 -1
- package/dist/config/schema.mjs +3 -1
- package/dist/config/schema.mjs.map +1 -1
- package/dist/config/uri-parser.cjs +195 -2
- package/dist/config/uri-parser.mjs +195 -2
- package/dist/config/uri-parser.mjs.map +1 -1
- package/dist/explorer/actions.cjs +53 -23
- package/dist/explorer/actions.mjs +54 -23
- package/dist/explorer/actions.mjs.map +1 -1
- package/dist/explorer/components/dialog.cjs +163 -10
- package/dist/explorer/components/dialog.mjs +163 -10
- package/dist/explorer/components/dialog.mjs.map +1 -1
- package/dist/explorer/components/file-list.mjs.map +1 -1
- package/dist/explorer/components/metadata-panel.cjs +39 -25
- package/dist/explorer/components/metadata-panel.mjs +39 -25
- package/dist/explorer/components/metadata-panel.mjs.map +1 -1
- package/dist/explorer/screen.cjs +23 -8
- package/dist/explorer/screen.mjs +24 -9
- package/dist/explorer/screen.mjs.map +1 -1
- package/dist/explorer/theme.cjs +3 -1
- package/dist/explorer/theme.mjs +3 -1
- package/dist/explorer/theme.mjs.map +1 -1
- package/dist/path-utils.cjs +2 -1
- package/dist/path-utils.mjs +1 -1
- package/dist/runtime.cjs +24 -0
- package/dist/runtime.mjs +24 -0
- package/dist/runtime.mjs.map +1 -1
- package/dist/ui/header.cjs +0 -9
- package/dist/ui/header.mjs +1 -9
- package/dist/ui/header.mjs.map +1 -1
- package/dist/ui/index.cjs +0 -2
- package/dist/ui/index.mjs +2 -3
- package/dist/ui/index.mjs.map +1 -1
- package/dist/utils/meta.cjs +51 -0
- package/dist/utils/meta.mjs +49 -0
- package/dist/utils/meta.mjs.map +1 -0
- package/package.json +19 -9
package/README.md
CHANGED
|
@@ -59,10 +59,11 @@ access_mode = "readonly"
|
|
|
59
59
|
path = "/db"
|
|
60
60
|
uri = "sqlite:///path/to/app.db"
|
|
61
61
|
|
|
62
|
-
# 远程 AFS 服务器
|
|
62
|
+
# 远程 AFS 服务器 (带认证)
|
|
63
63
|
[[mounts]]
|
|
64
64
|
path = "/remote"
|
|
65
65
|
uri = "https://afs.example.com/afs"
|
|
66
|
+
token = "${AFS_TOKEN}" # 使用环境变量
|
|
66
67
|
|
|
67
68
|
# HTTP 服务器配置
|
|
68
69
|
[serve]
|
|
@@ -72,8 +73,22 @@ path = "/afs"
|
|
|
72
73
|
readonly = false
|
|
73
74
|
cors = false
|
|
74
75
|
max_body_size = 10485760 # 10MB
|
|
76
|
+
token = "${AFS_SERVER_TOKEN}" # 服务器认证 token
|
|
75
77
|
```
|
|
76
78
|
|
|
79
|
+
### 挂载配置字段
|
|
80
|
+
|
|
81
|
+
| 字段 | 必填 | 说明 |
|
|
82
|
+
|------|------|------|
|
|
83
|
+
| `path` | 是 | 挂载路径 (如 `/src`) |
|
|
84
|
+
| `uri` | 是 | Provider URI (如 `fs:///path`) |
|
|
85
|
+
| `description` | 否 | 人类/LLM 可读描述 |
|
|
86
|
+
| `access_mode` | 否 | 访问模式: `readonly` 或 `readwrite` |
|
|
87
|
+
| `token` | 否 | HTTP Provider 认证 token (支持 `${ENV_VAR}`) |
|
|
88
|
+
| `auth` | 否 | 认证字符串 (支持 `${ENV_VAR}`) |
|
|
89
|
+
| `namespace` | 否 | 命名空间 (用于隔离不同环境) |
|
|
90
|
+
| `options` | 否 | Provider 特定选项 |
|
|
91
|
+
|
|
77
92
|
## 命令
|
|
78
93
|
|
|
79
94
|
### 文件操作
|
package/dist/cli.cjs
CHANGED
|
@@ -122,13 +122,15 @@ async function main() {
|
|
|
122
122
|
}), async (argv) => {
|
|
123
123
|
const view = getViewType(argv);
|
|
124
124
|
await maybeShowHeader(view);
|
|
125
|
-
const
|
|
125
|
+
const runtime = await require_runtime.createRuntime();
|
|
126
|
+
const path = argv.path;
|
|
127
|
+
const result = await require_ls.lsCommand(runtime, path, {
|
|
126
128
|
maxDepth: argv.depth,
|
|
127
129
|
limit: argv.limit,
|
|
128
130
|
maxChildren: argv["max-children"],
|
|
129
131
|
pattern: argv.pattern
|
|
130
132
|
});
|
|
131
|
-
console.log(require_ls.formatLsOutput(result, view));
|
|
133
|
+
console.log(require_ls.formatLsOutput(result, view, { path }));
|
|
132
134
|
}).command("stat <path>", "Get file or directory info", (yargs$2) => yargs$2.positional("path", {
|
|
133
135
|
type: "string",
|
|
134
136
|
demandOption: true,
|
|
@@ -158,35 +160,45 @@ async function main() {
|
|
|
158
160
|
type: "boolean",
|
|
159
161
|
default: false,
|
|
160
162
|
description: "Append to file instead of overwrite"
|
|
163
|
+
}).option("set", {
|
|
164
|
+
type: "array",
|
|
165
|
+
string: true,
|
|
166
|
+
description: "Set metadata field (format: key=value), can be repeated"
|
|
161
167
|
}), async (argv) => {
|
|
162
168
|
const view = getViewType(argv);
|
|
163
169
|
await maybeShowHeader(view);
|
|
164
170
|
let content;
|
|
165
171
|
if (argv.content !== void 0) content = argv.content;
|
|
172
|
+
else if (argv.set && argv.set.length > 0) content = void 0;
|
|
166
173
|
else {
|
|
167
174
|
const chunks = [];
|
|
168
175
|
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
169
176
|
content = Buffer.concat(chunks).toString("utf-8");
|
|
170
177
|
}
|
|
171
|
-
const result = await require_write.writeCommand(await require_runtime.createRuntime(), argv.path, content, {
|
|
178
|
+
const result = await require_write.writeCommand(await require_runtime.createRuntime(), argv.path, content, {
|
|
179
|
+
append: argv.append,
|
|
180
|
+
set: argv.set
|
|
181
|
+
});
|
|
172
182
|
console.log(require_write.formatWriteOutput(result, view));
|
|
173
183
|
if (!result.success) process.exit(require_errors.ExitCode.RUNTIME_ERROR);
|
|
174
|
-
}).command("exec <path>
|
|
184
|
+
}).command("exec <path>", "Execute an executable path (e.g., MCP tool)", (yargs$2) => yargs$2.positional("path", {
|
|
175
185
|
type: "string",
|
|
176
186
|
demandOption: true,
|
|
177
|
-
description: "Path to execute
|
|
178
|
-
}).
|
|
179
|
-
type: "string",
|
|
180
|
-
default: "default",
|
|
181
|
-
description: "Action to execute"
|
|
182
|
-
}).option("params", {
|
|
187
|
+
description: "Path to execute (must be executable)"
|
|
188
|
+
}).option("args", {
|
|
183
189
|
type: "string",
|
|
184
|
-
description: "JSON
|
|
185
|
-
}),
|
|
186
|
-
|
|
190
|
+
description: "JSON arguments: --args '{\"key\": \"value\"}'"
|
|
191
|
+
}).option("yaml", {
|
|
192
|
+
type: "boolean",
|
|
193
|
+
description: "Output in YAML format"
|
|
194
|
+
}).example("$0 exec /modules/mcp/tools/echo --message hello", "Execute with named param").example("$0 exec /modules/mcp/tools/query --args '{\"sql\": \"SELECT 1\"}'", "Execute with JSON args").strict(false), async (argv) => {
|
|
195
|
+
let view = "human";
|
|
196
|
+
if (argv.json) view = "json";
|
|
197
|
+
else if (argv.yaml) view = "yaml";
|
|
198
|
+
else if (argv.view) view = argv.view;
|
|
187
199
|
await maybeShowHeader(view);
|
|
188
|
-
const
|
|
189
|
-
const result = await require_exec.execCommand(await require_runtime.createRuntime(), argv.path,
|
|
200
|
+
const args = await require_exec.parseExecArgsWithStdin(argv);
|
|
201
|
+
const result = await require_exec.execCommand(await require_runtime.createRuntime(), argv.path, args);
|
|
190
202
|
console.log(require_exec.formatExecOutput(result, view));
|
|
191
203
|
if (!result.success) process.exit(require_errors.ExitCode.RUNTIME_ERROR);
|
|
192
204
|
}).command("mount", "Manage mount configurations", (yargs$2) => yargs$2.command(["list", "ls"], "List all mounts", () => {}, async (argv) => {
|
|
@@ -205,10 +217,16 @@ async function main() {
|
|
|
205
217
|
}).option("description", {
|
|
206
218
|
type: "string",
|
|
207
219
|
description: "Human-readable description for this mount"
|
|
220
|
+
}).option("token", {
|
|
221
|
+
type: "string",
|
|
222
|
+
description: "Bearer token for HTTP provider authorization"
|
|
208
223
|
}), async (argv) => {
|
|
209
224
|
const view = getViewType(argv);
|
|
210
225
|
await maybeShowHeader(view);
|
|
211
|
-
const result = await require_mount.mountAddCommand(process.cwd(), argv.path, argv.uri, {
|
|
226
|
+
const result = await require_mount.mountAddCommand(process.cwd(), argv.path, argv.uri, {
|
|
227
|
+
description: argv.description,
|
|
228
|
+
token: argv.token
|
|
229
|
+
});
|
|
212
230
|
if (view === "json") console.log(JSON.stringify(result, null, 2));
|
|
213
231
|
else if (result.success) {
|
|
214
232
|
const msg = view === "human" ? `${require_index.colors.success("Added mount")} ${require_index.colors.cyan(result.normalizedPath)}` : `Added mount ${result.normalizedPath}`;
|
|
@@ -289,6 +307,9 @@ async function main() {
|
|
|
289
307
|
}).option("max-body", {
|
|
290
308
|
type: "number",
|
|
291
309
|
description: "Maximum request body size in bytes (default: 10MB)"
|
|
310
|
+
}).option("token", {
|
|
311
|
+
type: "string",
|
|
312
|
+
description: "Bearer token for authorization"
|
|
292
313
|
}), async (argv) => {
|
|
293
314
|
await maybeShowHeader(getViewType(argv));
|
|
294
315
|
const result = await require_serve.serveCommand({
|
|
@@ -297,7 +318,8 @@ async function main() {
|
|
|
297
318
|
path: argv.path,
|
|
298
319
|
readonly: argv.readonly,
|
|
299
320
|
cors: argv.cors,
|
|
300
|
-
maxBodySize: argv["max-body"]
|
|
321
|
+
maxBodySize: argv["max-body"],
|
|
322
|
+
token: argv.token
|
|
301
323
|
});
|
|
302
324
|
console.log(require_serve.formatServeOutput(result));
|
|
303
325
|
await new Promise(() => {});
|
|
@@ -325,9 +347,20 @@ async function main() {
|
|
|
325
347
|
throw error;
|
|
326
348
|
}
|
|
327
349
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
350
|
+
/**
|
|
351
|
+
* Format error message for display
|
|
352
|
+
*/
|
|
353
|
+
function formatErrorForDisplay(error) {
|
|
354
|
+
const message = error.message;
|
|
355
|
+
const lines = message.split("\n");
|
|
356
|
+
if (lines.length > 1) return lines.map((line, i) => i === 0 ? `${require_index.colors.red("Error:")} ${line}` : ` ${line}`).join("\n");
|
|
357
|
+
return `${require_index.colors.red("Error:")} ${message}`;
|
|
358
|
+
}
|
|
359
|
+
main().then(() => {
|
|
360
|
+
process.exit(0);
|
|
361
|
+
}).catch((error) => {
|
|
362
|
+
console.error(formatErrorForDisplay(error));
|
|
363
|
+
process.exit(error instanceof require_errors.CLIError ? error.exitCode : require_errors.ExitCode.RUNTIME_ERROR);
|
|
331
364
|
});
|
|
332
365
|
|
|
333
366
|
//#endregion
|
package/dist/cli.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { VERSION } from "./version.mjs";
|
|
3
|
-
import { execCommand, formatExecOutput } from "./commands/exec.mjs";
|
|
3
|
+
import { execCommand, formatExecOutput, parseExecArgsWithStdin } from "./commands/exec.mjs";
|
|
4
4
|
import { ConfigLoader } from "./config/loader.mjs";
|
|
5
5
|
import { colors, printHeader, shouldShowHeader } from "./ui/index.mjs";
|
|
6
6
|
import { formatMountListOutput, mountAddCommand, mountListCommand, mountRemoveCommand, mountValidateCommand } from "./commands/mount.mjs";
|
|
@@ -120,13 +120,15 @@ async function main() {
|
|
|
120
120
|
}), async (argv) => {
|
|
121
121
|
const view = getViewType(argv);
|
|
122
122
|
await maybeShowHeader(view);
|
|
123
|
-
const
|
|
123
|
+
const runtime = await createRuntime();
|
|
124
|
+
const path = argv.path;
|
|
125
|
+
const result = await lsCommand(runtime, path, {
|
|
124
126
|
maxDepth: argv.depth,
|
|
125
127
|
limit: argv.limit,
|
|
126
128
|
maxChildren: argv["max-children"],
|
|
127
129
|
pattern: argv.pattern
|
|
128
130
|
});
|
|
129
|
-
console.log(formatLsOutput(result, view));
|
|
131
|
+
console.log(formatLsOutput(result, view, { path }));
|
|
130
132
|
}).command("stat <path>", "Get file or directory info", (yargs$1) => yargs$1.positional("path", {
|
|
131
133
|
type: "string",
|
|
132
134
|
demandOption: true,
|
|
@@ -156,35 +158,45 @@ async function main() {
|
|
|
156
158
|
type: "boolean",
|
|
157
159
|
default: false,
|
|
158
160
|
description: "Append to file instead of overwrite"
|
|
161
|
+
}).option("set", {
|
|
162
|
+
type: "array",
|
|
163
|
+
string: true,
|
|
164
|
+
description: "Set metadata field (format: key=value), can be repeated"
|
|
159
165
|
}), async (argv) => {
|
|
160
166
|
const view = getViewType(argv);
|
|
161
167
|
await maybeShowHeader(view);
|
|
162
168
|
let content;
|
|
163
169
|
if (argv.content !== void 0) content = argv.content;
|
|
170
|
+
else if (argv.set && argv.set.length > 0) content = void 0;
|
|
164
171
|
else {
|
|
165
172
|
const chunks = [];
|
|
166
173
|
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
167
174
|
content = Buffer.concat(chunks).toString("utf-8");
|
|
168
175
|
}
|
|
169
|
-
const result = await writeCommand(await createRuntime(), argv.path, content, {
|
|
176
|
+
const result = await writeCommand(await createRuntime(), argv.path, content, {
|
|
177
|
+
append: argv.append,
|
|
178
|
+
set: argv.set
|
|
179
|
+
});
|
|
170
180
|
console.log(formatWriteOutput(result, view));
|
|
171
181
|
if (!result.success) process.exit(ExitCode.RUNTIME_ERROR);
|
|
172
|
-
}).command("exec <path>
|
|
182
|
+
}).command("exec <path>", "Execute an executable path (e.g., MCP tool)", (yargs$1) => yargs$1.positional("path", {
|
|
173
183
|
type: "string",
|
|
174
184
|
demandOption: true,
|
|
175
|
-
description: "Path to execute
|
|
176
|
-
}).
|
|
177
|
-
type: "string",
|
|
178
|
-
default: "default",
|
|
179
|
-
description: "Action to execute"
|
|
180
|
-
}).option("params", {
|
|
185
|
+
description: "Path to execute (must be executable)"
|
|
186
|
+
}).option("args", {
|
|
181
187
|
type: "string",
|
|
182
|
-
description: "JSON
|
|
183
|
-
}),
|
|
184
|
-
|
|
188
|
+
description: "JSON arguments: --args '{\"key\": \"value\"}'"
|
|
189
|
+
}).option("yaml", {
|
|
190
|
+
type: "boolean",
|
|
191
|
+
description: "Output in YAML format"
|
|
192
|
+
}).example("$0 exec /modules/mcp/tools/echo --message hello", "Execute with named param").example("$0 exec /modules/mcp/tools/query --args '{\"sql\": \"SELECT 1\"}'", "Execute with JSON args").strict(false), async (argv) => {
|
|
193
|
+
let view = "human";
|
|
194
|
+
if (argv.json) view = "json";
|
|
195
|
+
else if (argv.yaml) view = "yaml";
|
|
196
|
+
else if (argv.view) view = argv.view;
|
|
185
197
|
await maybeShowHeader(view);
|
|
186
|
-
const
|
|
187
|
-
const result = await execCommand(await createRuntime(), argv.path,
|
|
198
|
+
const args = await parseExecArgsWithStdin(argv);
|
|
199
|
+
const result = await execCommand(await createRuntime(), argv.path, args);
|
|
188
200
|
console.log(formatExecOutput(result, view));
|
|
189
201
|
if (!result.success) process.exit(ExitCode.RUNTIME_ERROR);
|
|
190
202
|
}).command("mount", "Manage mount configurations", (yargs$1) => yargs$1.command(["list", "ls"], "List all mounts", () => {}, async (argv) => {
|
|
@@ -203,10 +215,16 @@ async function main() {
|
|
|
203
215
|
}).option("description", {
|
|
204
216
|
type: "string",
|
|
205
217
|
description: "Human-readable description for this mount"
|
|
218
|
+
}).option("token", {
|
|
219
|
+
type: "string",
|
|
220
|
+
description: "Bearer token for HTTP provider authorization"
|
|
206
221
|
}), async (argv) => {
|
|
207
222
|
const view = getViewType(argv);
|
|
208
223
|
await maybeShowHeader(view);
|
|
209
|
-
const result = await mountAddCommand(process.cwd(), argv.path, argv.uri, {
|
|
224
|
+
const result = await mountAddCommand(process.cwd(), argv.path, argv.uri, {
|
|
225
|
+
description: argv.description,
|
|
226
|
+
token: argv.token
|
|
227
|
+
});
|
|
210
228
|
if (view === "json") console.log(JSON.stringify(result, null, 2));
|
|
211
229
|
else if (result.success) {
|
|
212
230
|
const msg = view === "human" ? `${colors.success("Added mount")} ${colors.cyan(result.normalizedPath)}` : `Added mount ${result.normalizedPath}`;
|
|
@@ -287,6 +305,9 @@ async function main() {
|
|
|
287
305
|
}).option("max-body", {
|
|
288
306
|
type: "number",
|
|
289
307
|
description: "Maximum request body size in bytes (default: 10MB)"
|
|
308
|
+
}).option("token", {
|
|
309
|
+
type: "string",
|
|
310
|
+
description: "Bearer token for authorization"
|
|
290
311
|
}), async (argv) => {
|
|
291
312
|
await maybeShowHeader(getViewType(argv));
|
|
292
313
|
const result = await serveCommand({
|
|
@@ -295,7 +316,8 @@ async function main() {
|
|
|
295
316
|
path: argv.path,
|
|
296
317
|
readonly: argv.readonly,
|
|
297
318
|
cors: argv.cors,
|
|
298
|
-
maxBodySize: argv["max-body"]
|
|
319
|
+
maxBodySize: argv["max-body"],
|
|
320
|
+
token: argv.token
|
|
299
321
|
});
|
|
300
322
|
console.log(formatServeOutput(result));
|
|
301
323
|
await new Promise(() => {});
|
|
@@ -323,9 +345,20 @@ async function main() {
|
|
|
323
345
|
throw error;
|
|
324
346
|
}
|
|
325
347
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
348
|
+
/**
|
|
349
|
+
* Format error message for display
|
|
350
|
+
*/
|
|
351
|
+
function formatErrorForDisplay(error) {
|
|
352
|
+
const message = error.message;
|
|
353
|
+
const lines = message.split("\n");
|
|
354
|
+
if (lines.length > 1) return lines.map((line, i) => i === 0 ? `${colors.red("Error:")} ${line}` : ` ${line}`).join("\n");
|
|
355
|
+
return `${colors.red("Error:")} ${message}`;
|
|
356
|
+
}
|
|
357
|
+
main().then(() => {
|
|
358
|
+
process.exit(0);
|
|
359
|
+
}).catch((error) => {
|
|
360
|
+
console.error(formatErrorForDisplay(error));
|
|
361
|
+
process.exit(error instanceof CLIError ? error.exitCode : ExitCode.RUNTIME_ERROR);
|
|
329
362
|
});
|
|
330
363
|
|
|
331
364
|
//#endregion
|
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 * - 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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"}
|
|
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 type ExecViewType,\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 parseExecArgsWithStdin,\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 path = argv.path!;\n const result = await lsCommand(runtime, 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, { path }));\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 .option(\"set\", {\n type: \"array\",\n string: true,\n description: \"Set metadata field (format: key=value), can be repeated\",\n }),\n async (argv) => {\n const view = getViewType(argv);\n await maybeShowHeader(view);\n\n let content: string | undefined;\n\n if (argv.content !== undefined) {\n // Use --content parameter\n content = argv.content;\n } else if (argv.set && argv.set.length > 0) {\n // If only --set is provided without content, content is optional\n content = undefined;\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 set: argv.set as string[] | undefined,\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>\",\n \"Execute an executable path (e.g., MCP tool)\",\n (yargs) =>\n yargs\n .positional(\"path\", {\n type: \"string\",\n demandOption: true,\n description: \"Path to execute (must be executable)\",\n })\n .option(\"args\", {\n type: \"string\",\n description: 'JSON arguments: --args \\'{\"key\": \"value\"}\\'',\n })\n .option(\"yaml\", {\n type: \"boolean\",\n description: \"Output in YAML format\",\n })\n .example(\"$0 exec /modules/mcp/tools/echo --message hello\", \"Execute with named param\")\n .example(\n '$0 exec /modules/mcp/tools/query --args \\'{\"sql\": \"SELECT 1\"}\\'',\n \"Execute with JSON args\",\n )\n .strict(false), // Allow unknown options as exec arguments\n async (argv) => {\n // Determine output format\n let view: ExecViewType = \"human\";\n if (argv.json) view = \"json\";\n else if (argv.yaml) view = \"yaml\";\n else if (argv.view) view = argv.view as ExecViewType;\n\n await maybeShowHeader(view as ViewType);\n\n // Parse arguments from CLI options with stdin support\n const args = await parseExecArgsWithStdin(argv as Record<string, unknown>);\n\n const runtime = await createRuntime();\n const result = await execCommand(runtime, argv.path!, args);\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 .option(\"token\", {\n type: \"string\",\n description: \"Bearer token for HTTP provider authorization\",\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 token: argv.token,\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 .option(\"token\", {\n type: \"string\",\n description: \"Bearer token for authorization\",\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 token: argv.token,\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\n/**\n * Format error message for display\n */\nfunction formatErrorForDisplay(error: Error): string {\n const message = error.message;\n\n // Format multi-line messages with proper indentation\n const lines = message.split(\"\\n\");\n if (lines.length > 1) {\n return lines\n .map((line, i) => (i === 0 ? `${colors.red(\"Error:\")} ${line}` : ` ${line}`))\n .join(\"\\n\");\n }\n\n return `${colors.red(\"Error:\")} ${message}`;\n}\n\nmain()\n .then(() => {\n // Force exit after command completes to ensure child processes (like MCP stdio)\n // don't keep the process alive. Long-running commands (serve, explore) handle\n // their own lifecycle and never reach here.\n process.exit(0);\n })\n .catch((error) => {\n console.error(formatErrorForDisplay(error));\n process.exit(error instanceof CLIError ? error.exitCode : ExitCode.RUNTIME_ERROR);\n });\n"],"mappings":"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"}
|
package/dist/commands/exec.cjs
CHANGED
|
@@ -1,17 +1,110 @@
|
|
|
1
|
+
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
|
|
2
|
+
let yaml = require("yaml");
|
|
3
|
+
yaml = require_rolldown_runtime.__toESM(yaml);
|
|
1
4
|
|
|
2
5
|
//#region src/commands/exec.ts
|
|
3
6
|
/**
|
|
7
|
+
* Reserved CLI option names that should not be passed as exec arguments
|
|
8
|
+
*/
|
|
9
|
+
const RESERVED_OPTIONS = new Set([
|
|
10
|
+
"json",
|
|
11
|
+
"yaml",
|
|
12
|
+
"view",
|
|
13
|
+
"help",
|
|
14
|
+
"h",
|
|
15
|
+
"version",
|
|
16
|
+
"V",
|
|
17
|
+
"_",
|
|
18
|
+
"$0",
|
|
19
|
+
"args"
|
|
20
|
+
]);
|
|
21
|
+
/**
|
|
22
|
+
* Read JSON from stdin (non-blocking if TTY)
|
|
23
|
+
*/
|
|
24
|
+
async function readStdin() {
|
|
25
|
+
if (process.stdin.isTTY) return null;
|
|
26
|
+
const chunks = [];
|
|
27
|
+
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
28
|
+
return Buffer.concat(chunks).toString("utf-8").trim() || null;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Parse exec arguments with stdin support (async version)
|
|
32
|
+
*
|
|
33
|
+
* Priority: CLI flags > stdin > --args
|
|
34
|
+
*/
|
|
35
|
+
async function parseExecArgsWithStdin(options) {
|
|
36
|
+
let result = {};
|
|
37
|
+
if (options.args && typeof options.args === "string") try {
|
|
38
|
+
const parsed = JSON.parse(options.args);
|
|
39
|
+
if (typeof parsed === "object" && parsed !== null) result = { ...parsed };
|
|
40
|
+
} catch (e) {
|
|
41
|
+
throw new Error(`Invalid JSON in --args: ${e.message}`);
|
|
42
|
+
}
|
|
43
|
+
const stdinContent = await readStdin();
|
|
44
|
+
if (stdinContent) try {
|
|
45
|
+
const parsed = JSON.parse(stdinContent);
|
|
46
|
+
if (typeof parsed === "object" && parsed !== null) result = {
|
|
47
|
+
...result,
|
|
48
|
+
...parsed
|
|
49
|
+
};
|
|
50
|
+
} catch (e) {
|
|
51
|
+
throw new Error(`Invalid JSON in stdin: ${e.message}`);
|
|
52
|
+
}
|
|
53
|
+
for (const [key, value] of Object.entries(options)) {
|
|
54
|
+
if (RESERVED_OPTIONS.has(key) || value === void 0 || value === null) continue;
|
|
55
|
+
result[key] = value;
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Check if an entry is executable based on its metadata
|
|
61
|
+
*
|
|
62
|
+
* An entry is executable if:
|
|
63
|
+
* 1. Its `kinds` array includes "afs:executable", OR
|
|
64
|
+
* 2. Its `kind` field equals "afs:executable" (legacy support)
|
|
65
|
+
*/
|
|
66
|
+
function isExecutable(metadata) {
|
|
67
|
+
if (!metadata) return false;
|
|
68
|
+
if (Array.isArray(metadata.kinds)) return metadata.kinds.includes("afs:executable");
|
|
69
|
+
return metadata.kind === "afs:executable";
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
4
72
|
* Execute an action on an AFS path
|
|
5
73
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
74
|
+
* This function directly executes the path without pre-checking if it's executable.
|
|
75
|
+
* The provider will return an appropriate error if the path is not executable.
|
|
76
|
+
*
|
|
77
|
+
* @param runtime - AFS runtime
|
|
78
|
+
* @param path - Path to execute
|
|
79
|
+
* @param args - Arguments to pass to the executable
|
|
80
|
+
* @returns Exec result
|
|
81
|
+
*/
|
|
82
|
+
async function execCommand(runtime, path, args = {}) {
|
|
83
|
+
try {
|
|
84
|
+
const execResult = await runtime.exec(path, args);
|
|
85
|
+
return {
|
|
86
|
+
path,
|
|
87
|
+
success: execResult.success,
|
|
88
|
+
data: execResult.data,
|
|
89
|
+
message: execResult.error?.message,
|
|
90
|
+
error: execResult.error
|
|
91
|
+
};
|
|
92
|
+
} catch (error) {
|
|
93
|
+
const errorCode = extractErrorCode(error);
|
|
94
|
+
return {
|
|
95
|
+
path,
|
|
96
|
+
success: false,
|
|
97
|
+
message: error instanceof Error ? error.message : String(error),
|
|
98
|
+
error: errorCode ? { code: errorCode } : void 0
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Extract error code from an error object
|
|
8
104
|
*/
|
|
9
|
-
|
|
10
|
-
return
|
|
11
|
-
|
|
12
|
-
success: false,
|
|
13
|
-
message: "exec operation is not yet implemented"
|
|
14
|
-
};
|
|
105
|
+
function extractErrorCode(error) {
|
|
106
|
+
if (!error || typeof error !== "object") return void 0;
|
|
107
|
+
if ("code" in error && typeof error.code === "string") return error.code;
|
|
15
108
|
}
|
|
16
109
|
/**
|
|
17
110
|
* Format exec output for different views
|
|
@@ -19,28 +112,53 @@ async function execCommand(_runtime, path, _action, _params = {}) {
|
|
|
19
112
|
function formatExecOutput(result, view) {
|
|
20
113
|
switch (view) {
|
|
21
114
|
case "json": return JSON.stringify(result, null, 2);
|
|
115
|
+
case "yaml": return yaml.default.stringify(result);
|
|
22
116
|
case "llm": return formatLlm(result);
|
|
23
117
|
case "human": return formatHuman(result);
|
|
24
118
|
default: return formatDefault(result);
|
|
25
119
|
}
|
|
26
120
|
}
|
|
27
121
|
function formatDefault(result) {
|
|
28
|
-
if (result.success)
|
|
29
|
-
|
|
122
|
+
if (result.success) {
|
|
123
|
+
if (result.data) return JSON.stringify(result.data);
|
|
124
|
+
return `OK ${result.path}`;
|
|
125
|
+
}
|
|
126
|
+
return `${result.error?.code || "ERROR"} ${result.path} ${result.message || ""}`.trim();
|
|
30
127
|
}
|
|
31
128
|
function formatLlm(result) {
|
|
32
129
|
const lines = [];
|
|
33
130
|
lines.push(`EXEC ${result.path}`);
|
|
34
131
|
lines.push(`STATUS ${result.success ? "SUCCESS" : "FAILED"}`);
|
|
35
132
|
if (result.data) lines.push(`DATA ${JSON.stringify(result.data)}`);
|
|
36
|
-
if (result.
|
|
133
|
+
if (!result.success && result.error) {
|
|
134
|
+
if (result.error.code) lines.push(`ERROR ${result.error.code}`);
|
|
135
|
+
if (result.message) lines.push(`MESSAGE ${result.message}`);
|
|
136
|
+
if (result.error.details) lines.push(`DETAILS ${JSON.stringify(result.error.details)}`);
|
|
137
|
+
} else if (result.message) lines.push(`MESSAGE ${result.message}`);
|
|
37
138
|
return lines.join("\n");
|
|
38
139
|
}
|
|
39
140
|
function formatHuman(result) {
|
|
40
|
-
if (result.success)
|
|
41
|
-
|
|
141
|
+
if (result.success) {
|
|
142
|
+
const lines$1 = [`✓ Executed: ${result.path}`];
|
|
143
|
+
if (result.data) {
|
|
144
|
+
lines$1.push("");
|
|
145
|
+
lines$1.push(JSON.stringify(result.data, null, 2));
|
|
146
|
+
}
|
|
147
|
+
return lines$1.join("\n");
|
|
148
|
+
}
|
|
149
|
+
const lines = [`✗ Failed: ${result.path}`];
|
|
150
|
+
lines.push("");
|
|
151
|
+
if (result.error?.code) lines.push(`Error: ${result.error.code}`);
|
|
152
|
+
if (result.message) lines.push(`Message: ${result.message}`);
|
|
153
|
+
if (result.error?.details) {
|
|
154
|
+
lines.push("Details:");
|
|
155
|
+
for (const [key, value] of Object.entries(result.error.details)) lines.push(` ${key}: ${JSON.stringify(value)}`);
|
|
156
|
+
}
|
|
157
|
+
return lines.join("\n");
|
|
42
158
|
}
|
|
43
159
|
|
|
44
160
|
//#endregion
|
|
45
161
|
exports.execCommand = execCommand;
|
|
46
|
-
exports.formatExecOutput = formatExecOutput;
|
|
162
|
+
exports.formatExecOutput = formatExecOutput;
|
|
163
|
+
exports.isExecutable = isExecutable;
|
|
164
|
+
exports.parseExecArgsWithStdin = parseExecArgsWithStdin;
|