@aigne/afs-cli 1.11.0-beta.5 → 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/dist/cli.cjs +41 -18
- package/dist/cli.mjs +42 -19
- 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/read.cjs +213 -14
- package/dist/commands/read.mjs +213 -14
- package/dist/commands/read.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 +12 -1
- package/dist/config/loader.mjs +12 -1
- package/dist/config/loader.mjs.map +1 -1
- package/dist/config/provider-factory.cjs +310 -3
- package/dist/config/provider-factory.mjs +310 -3
- package/dist/config/provider-factory.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/dist/commands/exec.mjs
CHANGED
|
@@ -1,16 +1,108 @@
|
|
|
1
|
+
import YAML from "yaml";
|
|
2
|
+
|
|
1
3
|
//#region src/commands/exec.ts
|
|
2
4
|
/**
|
|
5
|
+
* Reserved CLI option names that should not be passed as exec arguments
|
|
6
|
+
*/
|
|
7
|
+
const RESERVED_OPTIONS = new Set([
|
|
8
|
+
"json",
|
|
9
|
+
"yaml",
|
|
10
|
+
"view",
|
|
11
|
+
"help",
|
|
12
|
+
"h",
|
|
13
|
+
"version",
|
|
14
|
+
"V",
|
|
15
|
+
"_",
|
|
16
|
+
"$0",
|
|
17
|
+
"args"
|
|
18
|
+
]);
|
|
19
|
+
/**
|
|
20
|
+
* Read JSON from stdin (non-blocking if TTY)
|
|
21
|
+
*/
|
|
22
|
+
async function readStdin() {
|
|
23
|
+
if (process.stdin.isTTY) return null;
|
|
24
|
+
const chunks = [];
|
|
25
|
+
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
26
|
+
return Buffer.concat(chunks).toString("utf-8").trim() || null;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Parse exec arguments with stdin support (async version)
|
|
30
|
+
*
|
|
31
|
+
* Priority: CLI flags > stdin > --args
|
|
32
|
+
*/
|
|
33
|
+
async function parseExecArgsWithStdin(options) {
|
|
34
|
+
let result = {};
|
|
35
|
+
if (options.args && typeof options.args === "string") try {
|
|
36
|
+
const parsed = JSON.parse(options.args);
|
|
37
|
+
if (typeof parsed === "object" && parsed !== null) result = { ...parsed };
|
|
38
|
+
} catch (e) {
|
|
39
|
+
throw new Error(`Invalid JSON in --args: ${e.message}`);
|
|
40
|
+
}
|
|
41
|
+
const stdinContent = await readStdin();
|
|
42
|
+
if (stdinContent) try {
|
|
43
|
+
const parsed = JSON.parse(stdinContent);
|
|
44
|
+
if (typeof parsed === "object" && parsed !== null) result = {
|
|
45
|
+
...result,
|
|
46
|
+
...parsed
|
|
47
|
+
};
|
|
48
|
+
} catch (e) {
|
|
49
|
+
throw new Error(`Invalid JSON in stdin: ${e.message}`);
|
|
50
|
+
}
|
|
51
|
+
for (const [key, value] of Object.entries(options)) {
|
|
52
|
+
if (RESERVED_OPTIONS.has(key) || value === void 0 || value === null) continue;
|
|
53
|
+
result[key] = value;
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Check if an entry is executable based on its metadata
|
|
59
|
+
*
|
|
60
|
+
* An entry is executable if:
|
|
61
|
+
* 1. Its `kinds` array includes "afs:executable", OR
|
|
62
|
+
* 2. Its `kind` field equals "afs:executable" (legacy support)
|
|
63
|
+
*/
|
|
64
|
+
function isExecutable(metadata) {
|
|
65
|
+
if (!metadata) return false;
|
|
66
|
+
if (Array.isArray(metadata.kinds)) return metadata.kinds.includes("afs:executable");
|
|
67
|
+
return metadata.kind === "afs:executable";
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
3
70
|
* Execute an action on an AFS path
|
|
4
71
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
72
|
+
* This function directly executes the path without pre-checking if it's executable.
|
|
73
|
+
* The provider will return an appropriate error if the path is not executable.
|
|
74
|
+
*
|
|
75
|
+
* @param runtime - AFS runtime
|
|
76
|
+
* @param path - Path to execute
|
|
77
|
+
* @param args - Arguments to pass to the executable
|
|
78
|
+
* @returns Exec result
|
|
7
79
|
*/
|
|
8
|
-
async function execCommand(
|
|
9
|
-
|
|
10
|
-
path,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
80
|
+
async function execCommand(runtime, path, args = {}) {
|
|
81
|
+
try {
|
|
82
|
+
const execResult = await runtime.exec(path, args);
|
|
83
|
+
return {
|
|
84
|
+
path,
|
|
85
|
+
success: execResult.success,
|
|
86
|
+
data: execResult.data,
|
|
87
|
+
message: execResult.error?.message,
|
|
88
|
+
error: execResult.error
|
|
89
|
+
};
|
|
90
|
+
} catch (error) {
|
|
91
|
+
const errorCode = extractErrorCode(error);
|
|
92
|
+
return {
|
|
93
|
+
path,
|
|
94
|
+
success: false,
|
|
95
|
+
message: error instanceof Error ? error.message : String(error),
|
|
96
|
+
error: errorCode ? { code: errorCode } : void 0
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Extract error code from an error object
|
|
102
|
+
*/
|
|
103
|
+
function extractErrorCode(error) {
|
|
104
|
+
if (!error || typeof error !== "object") return void 0;
|
|
105
|
+
if ("code" in error && typeof error.code === "string") return error.code;
|
|
14
106
|
}
|
|
15
107
|
/**
|
|
16
108
|
* Format exec output for different views
|
|
@@ -18,28 +110,51 @@ async function execCommand(_runtime, path, _action, _params = {}) {
|
|
|
18
110
|
function formatExecOutput(result, view) {
|
|
19
111
|
switch (view) {
|
|
20
112
|
case "json": return JSON.stringify(result, null, 2);
|
|
113
|
+
case "yaml": return YAML.stringify(result);
|
|
21
114
|
case "llm": return formatLlm(result);
|
|
22
115
|
case "human": return formatHuman(result);
|
|
23
116
|
default: return formatDefault(result);
|
|
24
117
|
}
|
|
25
118
|
}
|
|
26
119
|
function formatDefault(result) {
|
|
27
|
-
if (result.success)
|
|
28
|
-
|
|
120
|
+
if (result.success) {
|
|
121
|
+
if (result.data) return JSON.stringify(result.data);
|
|
122
|
+
return `OK ${result.path}`;
|
|
123
|
+
}
|
|
124
|
+
return `${result.error?.code || "ERROR"} ${result.path} ${result.message || ""}`.trim();
|
|
29
125
|
}
|
|
30
126
|
function formatLlm(result) {
|
|
31
127
|
const lines = [];
|
|
32
128
|
lines.push(`EXEC ${result.path}`);
|
|
33
129
|
lines.push(`STATUS ${result.success ? "SUCCESS" : "FAILED"}`);
|
|
34
130
|
if (result.data) lines.push(`DATA ${JSON.stringify(result.data)}`);
|
|
35
|
-
if (result.
|
|
131
|
+
if (!result.success && result.error) {
|
|
132
|
+
if (result.error.code) lines.push(`ERROR ${result.error.code}`);
|
|
133
|
+
if (result.message) lines.push(`MESSAGE ${result.message}`);
|
|
134
|
+
if (result.error.details) lines.push(`DETAILS ${JSON.stringify(result.error.details)}`);
|
|
135
|
+
} else if (result.message) lines.push(`MESSAGE ${result.message}`);
|
|
36
136
|
return lines.join("\n");
|
|
37
137
|
}
|
|
38
138
|
function formatHuman(result) {
|
|
39
|
-
if (result.success)
|
|
40
|
-
|
|
139
|
+
if (result.success) {
|
|
140
|
+
const lines$1 = [`✓ Executed: ${result.path}`];
|
|
141
|
+
if (result.data) {
|
|
142
|
+
lines$1.push("");
|
|
143
|
+
lines$1.push(JSON.stringify(result.data, null, 2));
|
|
144
|
+
}
|
|
145
|
+
return lines$1.join("\n");
|
|
146
|
+
}
|
|
147
|
+
const lines = [`✗ Failed: ${result.path}`];
|
|
148
|
+
lines.push("");
|
|
149
|
+
if (result.error?.code) lines.push(`Error: ${result.error.code}`);
|
|
150
|
+
if (result.message) lines.push(`Message: ${result.message}`);
|
|
151
|
+
if (result.error?.details) {
|
|
152
|
+
lines.push("Details:");
|
|
153
|
+
for (const [key, value] of Object.entries(result.error.details)) lines.push(` ${key}: ${JSON.stringify(value)}`);
|
|
154
|
+
}
|
|
155
|
+
return lines.join("\n");
|
|
41
156
|
}
|
|
42
157
|
|
|
43
158
|
//#endregion
|
|
44
|
-
export { execCommand, formatExecOutput };
|
|
159
|
+
export { execCommand, formatExecOutput, isExecutable, parseExecArgsWithStdin };
|
|
45
160
|
//# sourceMappingURL=exec.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exec.mjs","names":[],"sources":["../../src/commands/exec.ts"],"sourcesContent":["import type { AFSRuntime } from \"../runtime.js\";\nimport type { ViewType } from \"./ls.js\";\n\nexport interface ExecResult {\n path: string;\n success: boolean;\n data?: Record<string, unknown>;\n message?: string;\n}\n\n/**\n * Execute an action on an AFS path\n *\n * Note: exec is not widely implemented by providers yet.\n * This is a placeholder for future functionality.\n */\nexport async function execCommand(\n _runtime: AFSRuntime,\n path: string,\n _action: string,\n _params: Record<string, unknown> = {},\n): Promise<ExecResult> {\n // exec is not yet implemented in the runtime\n // This is a placeholder that returns an error\n\n return {\n path,\n success: false,\n message: \"exec operation is not yet implemented\",\n };\n}\n\n/**\n * Format exec output for different views\n */\nexport function formatExecOutput(result: ExecResult, view: ViewType): string {\n switch (view) {\n case \"json\":\n return JSON.stringify(result, null, 2);\n case \"llm\":\n return formatLlm(result);\n case \"human\":\n return formatHuman(result);\n default:\n return formatDefault(result);\n }\n}\n\nfunction formatDefault(result: ExecResult): string {\n if (result.success) {\n return `OK ${result.path}`;\n }\n return `ERROR ${result.path} ${result.message}`;\n}\n\nfunction formatLlm(result: ExecResult): string {\n const lines: string[] = [];\n\n lines.push(`EXEC ${result.path}`);\n lines.push(`STATUS ${result.success ? \"SUCCESS\" : \"FAILED\"}`);\n\n if (result.data) {\n lines.push(`DATA ${JSON.stringify(result.data)}`);\n }\n\n if (result.message) {\n lines.push(`MESSAGE ${result.message}`);\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction formatHuman(result: ExecResult): string {\n if (result.success) {\n return `Executed on ${result.path}\\n${result.data ? JSON.stringify(result.data, null, 2) : \"\"}`;\n }\n return `Failed to execute on ${result.path}: ${result.message}`;\n}\n"],"mappings":";;;;;;;AAgBA,eAAsB,YACpB,UACA,MACA,SACA,UAAmC,EAAE,EAChB;AAIrB,QAAO;EACL;EACA,SAAS;EACT,SAAS;EACV;;;;;AAMH,SAAgB,iBAAiB,QAAoB,MAAwB;AAC3E,SAAQ,MAAR;EACE,KAAK,OACH,QAAO,KAAK,UAAU,QAAQ,MAAM,EAAE;EACxC,KAAK,MACH,QAAO,UAAU,OAAO;EAC1B,KAAK,QACH,QAAO,YAAY,OAAO;EAC5B,QACE,QAAO,cAAc,OAAO;;;AAIlC,SAAS,cAAc,QAA4B;AACjD,KAAI,OAAO,QACT,QAAO,MAAM,OAAO;AAEtB,QAAO,SAAS,OAAO,KAAK,GAAG,OAAO;;AAGxC,SAAS,UAAU,QAA4B;CAC7C,MAAM,QAAkB,EAAE;AAE1B,OAAM,KAAK,QAAQ,OAAO,OAAO;AACjC,OAAM,KAAK,UAAU,OAAO,UAAU,YAAY,WAAW;AAE7D,KAAI,OAAO,KACT,OAAM,KAAK,QAAQ,KAAK,UAAU,OAAO,KAAK,GAAG;AAGnD,KAAI,OAAO,QACT,OAAM,KAAK,WAAW,OAAO,UAAU;AAGzC,QAAO,MAAM,KAAK,KAAK;;AAGzB,SAAS,YAAY,QAA4B;AAC/C,KAAI,OAAO,QACT,QAAO,eAAe,OAAO,KAAK,IAAI,OAAO,OAAO,KAAK,UAAU,OAAO,MAAM,MAAM,EAAE,GAAG;AAE7F,QAAO,wBAAwB,OAAO,KAAK,IAAI,OAAO"}
|
|
1
|
+
{"version":3,"file":"exec.mjs","names":["lines"],"sources":["../../src/commands/exec.ts"],"sourcesContent":["import type { AFSEntryMetadata } from \"@aigne/afs\";\nimport YAML from \"yaml\";\nimport type { AFSRuntime } from \"../runtime.js\";\nimport type { ViewType } from \"./ls.js\";\n\n/**\n * Extended view type that includes yaml\n */\nexport type ExecViewType = ViewType | \"yaml\";\n\nexport interface ExecResult {\n path: string;\n success: boolean;\n data?: Record<string, unknown>;\n message?: string;\n error?: {\n code?: string;\n message?: string;\n details?: Record<string, unknown>;\n };\n}\n\n/**\n * Reserved CLI option names that should not be passed as exec arguments\n */\nconst RESERVED_OPTIONS = new Set([\n \"json\",\n \"yaml\",\n \"view\",\n \"help\",\n \"h\",\n \"version\",\n \"V\",\n \"_\", // yargs positional args array\n \"$0\", // yargs script name\n \"args\", // our --args option (processed separately)\n]);\n\n/**\n * Read JSON from stdin (non-blocking if TTY)\n */\nasync function readStdin(): Promise<string | null> {\n // Only read from stdin if it's not a TTY (i.e., there's pipe input)\n if (process.stdin.isTTY) {\n return null;\n }\n\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(chunk as Buffer);\n }\n const content = Buffer.concat(chunks).toString(\"utf-8\").trim();\n return content || null;\n}\n\n/**\n * Parse exec arguments from CLI options with stdin support\n *\n * Priority: CLI flags > stdin > --args\n *\n * Supports three input sources:\n * 1. JSON via --args: `afs exec /path --args '{\"key\": \"value\"}'`\n * 2. JSON via stdin: `echo '{\"key\": \"value\"}' | afs exec /path`\n * 3. Named parameters: `afs exec /path --sql \"SELECT * FROM users\" --limit 10`\n */\nexport function parseExecArgs(options: Record<string, unknown>): Record<string, unknown> {\n let result: Record<string, unknown> = {};\n\n // Parse --args JSON if provided (lowest priority)\n if (options.args && typeof options.args === \"string\") {\n try {\n const parsed = JSON.parse(options.args);\n if (typeof parsed === \"object\" && parsed !== null) {\n result = { ...parsed };\n }\n } catch (e) {\n throw new Error(`Invalid JSON in --args: ${(e as Error).message}`);\n }\n }\n\n // Add named parameters (overwrite JSON args if same key) - highest priority\n for (const [key, value] of Object.entries(options)) {\n // Skip reserved options and undefined/null values\n if (RESERVED_OPTIONS.has(key) || value === undefined || value === null) {\n continue;\n }\n result[key] = value;\n }\n\n return result;\n}\n\n/**\n * Parse exec arguments with stdin support (async version)\n *\n * Priority: CLI flags > stdin > --args\n */\nexport async function parseExecArgsWithStdin(\n options: Record<string, unknown>,\n): Promise<Record<string, unknown>> {\n let result: Record<string, unknown> = {};\n\n // 1. Parse --args JSON if provided (lowest priority)\n if (options.args && typeof options.args === \"string\") {\n try {\n const parsed = JSON.parse(options.args);\n if (typeof parsed === \"object\" && parsed !== null) {\n result = { ...parsed };\n }\n } catch (e) {\n throw new Error(`Invalid JSON in --args: ${(e as Error).message}`);\n }\n }\n\n // 2. Read from stdin if available (medium priority)\n const stdinContent = await readStdin();\n if (stdinContent) {\n try {\n const parsed = JSON.parse(stdinContent);\n if (typeof parsed === \"object\" && parsed !== null) {\n result = { ...result, ...parsed };\n }\n } catch (e) {\n throw new Error(`Invalid JSON in stdin: ${(e as Error).message}`);\n }\n }\n\n // 3. Add named CLI parameters (highest priority)\n for (const [key, value] of Object.entries(options)) {\n if (RESERVED_OPTIONS.has(key) || value === undefined || value === null) {\n continue;\n }\n result[key] = value;\n }\n\n return result;\n}\n\n/**\n * Check if an entry is executable based on its metadata\n *\n * An entry is executable if:\n * 1. Its `kinds` array includes \"afs:executable\", OR\n * 2. Its `kind` field equals \"afs:executable\" (legacy support)\n */\nexport function isExecutable(metadata: AFSEntryMetadata | null | undefined): boolean {\n if (!metadata) return false;\n\n // Check kinds array first (new way)\n if (Array.isArray(metadata.kinds)) {\n return metadata.kinds.includes(\"afs:executable\");\n }\n\n // Fall back to kind field (legacy)\n return metadata.kind === \"afs:executable\";\n}\n\n/**\n * Execute an action on an AFS path\n *\n * This function directly executes the path without pre-checking if it's executable.\n * The provider will return an appropriate error if the path is not executable.\n *\n * @param runtime - AFS runtime\n * @param path - Path to execute\n * @param args - Arguments to pass to the executable\n * @returns Exec result\n */\nexport async function execCommand(\n runtime: AFSRuntime,\n path: string,\n args: Record<string, unknown> = {},\n): Promise<ExecResult> {\n try {\n // Execute directly - let provider handle validation\n const execResult = await runtime.exec(path, args);\n\n return {\n path,\n success: execResult.success,\n data: execResult.data,\n message: execResult.error?.message,\n error: execResult.error,\n };\n } catch (error) {\n // Extract error code if available\n const errorCode = extractErrorCode(error);\n\n return {\n path,\n success: false,\n message: error instanceof Error ? error.message : String(error),\n error: errorCode ? { code: errorCode } : undefined,\n };\n }\n}\n\n/**\n * Extract error code from an error object\n */\nfunction extractErrorCode(error: unknown): string | undefined {\n if (!error || typeof error !== \"object\") return undefined;\n if (\"code\" in error && typeof (error as { code: unknown }).code === \"string\") {\n return (error as { code: string }).code;\n }\n return undefined;\n}\n\n/**\n * Format exec output for different views\n */\nexport function formatExecOutput(result: ExecResult, view: ExecViewType): string {\n switch (view) {\n case \"json\":\n return JSON.stringify(result, null, 2);\n case \"yaml\":\n return YAML.stringify(result);\n case \"llm\":\n return formatLlm(result);\n case \"human\":\n return formatHuman(result);\n default:\n return formatDefault(result);\n }\n}\n\nfunction formatDefault(result: ExecResult): string {\n if (result.success) {\n if (result.data) {\n return JSON.stringify(result.data);\n }\n return `OK ${result.path}`;\n }\n const errorCode = result.error?.code || \"ERROR\";\n return `${errorCode} ${result.path} ${result.message || \"\"}`.trim();\n}\n\nfunction formatLlm(result: ExecResult): string {\n const lines: string[] = [];\n\n lines.push(`EXEC ${result.path}`);\n lines.push(`STATUS ${result.success ? \"SUCCESS\" : \"FAILED\"}`);\n\n if (result.data) {\n lines.push(`DATA ${JSON.stringify(result.data)}`);\n }\n\n if (!result.success && result.error) {\n if (result.error.code) {\n lines.push(`ERROR ${result.error.code}`);\n }\n if (result.message) {\n lines.push(`MESSAGE ${result.message}`);\n }\n if (result.error.details) {\n lines.push(`DETAILS ${JSON.stringify(result.error.details)}`);\n }\n } else if (result.message) {\n lines.push(`MESSAGE ${result.message}`);\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction formatHuman(result: ExecResult): string {\n if (result.success) {\n const lines = [`✓ Executed: ${result.path}`];\n if (result.data) {\n lines.push(\"\");\n lines.push(JSON.stringify(result.data, null, 2));\n }\n return lines.join(\"\\n\");\n }\n\n const lines = [`✗ Failed: ${result.path}`];\n lines.push(\"\");\n if (result.error?.code) {\n lines.push(`Error: ${result.error.code}`);\n }\n if (result.message) {\n lines.push(`Message: ${result.message}`);\n }\n if (result.error?.details) {\n lines.push(\"Details:\");\n for (const [key, value] of Object.entries(result.error.details)) {\n lines.push(` ${key}: ${JSON.stringify(value)}`);\n }\n }\n return lines.join(\"\\n\");\n}\n"],"mappings":"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"}
|
|
@@ -24,7 +24,7 @@ async function explainPathCommand(runtime, path) {
|
|
|
24
24
|
type: "unknown"
|
|
25
25
|
};
|
|
26
26
|
const metadata = entry.metadata || {};
|
|
27
|
-
const entryType = metadata.
|
|
27
|
+
const entryType = metadata.kind === "afs:executable" ? "exec" : typeof metadata.childrenCount === "number" ? "directory" : "file";
|
|
28
28
|
const result = {
|
|
29
29
|
path: entry.path,
|
|
30
30
|
type: entryType,
|
|
@@ -24,7 +24,7 @@ async function explainPathCommand(runtime, path) {
|
|
|
24
24
|
type: "unknown"
|
|
25
25
|
};
|
|
26
26
|
const metadata = entry.metadata || {};
|
|
27
|
-
const entryType = metadata.
|
|
27
|
+
const entryType = metadata.kind === "afs:executable" ? "exec" : typeof metadata.childrenCount === "number" ? "directory" : "file";
|
|
28
28
|
const result = {
|
|
29
29
|
path: entry.path,
|
|
30
30
|
type: entryType,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"explain.mjs","names":[],"sources":["../../src/commands/explain.ts"],"sourcesContent":["import type { AFSRuntime } from \"../runtime.js\";\nimport type { ViewType } from \"./ls.js\";\nimport { mountListCommand } from \"./mount.js\";\n\nexport interface ExplainResult {\n topic: string;\n explanation: string;\n examples?: string[];\n}\n\nexport interface PathExplainResult {\n path: string;\n type: string;\n description?: string;\n inputs?: string[];\n outputs?: string[];\n errors?: string[];\n sideEffects?: string[];\n metadata?: Record<string, string>;\n}\n\n/**\n * Explain AFS concepts or current configuration\n */\nexport async function explainCommand(cwd: string, topic?: string): Promise<ExplainResult> {\n switch (topic) {\n case \"mount\":\n case \"mounts\":\n return explainMounts(cwd);\n case \"path\":\n case \"paths\":\n return explainPaths();\n case \"uri\":\n return explainUri();\n default:\n return explainOverview();\n }\n}\n\n/**\n * Explain an AFS object at a given path\n */\nexport async function explainPathCommand(\n runtime: AFSRuntime,\n path: string,\n): Promise<PathExplainResult> {\n const readResult = await runtime.read(path);\n const entry = readResult.data;\n\n if (!entry) {\n return {\n path,\n type: \"unknown\",\n };\n }\n\n const metadata = entry.metadata || {};\n const entryType = (metadata.type as string) || \"file\";\n\n const result: PathExplainResult = {\n path: entry.path,\n type: entryType,\n description: metadata.description as string | undefined,\n metadata: {},\n };\n\n // Add metadata\n if (metadata.provider) {\n result.metadata!.provider = String(metadata.provider);\n }\n if (metadata.permissions) {\n const perms = metadata.permissions;\n result.metadata!.permissions = Array.isArray(perms) ? perms.join(\", \") : String(perms);\n }\n\n // For exec type, try to get schema info\n if (entryType === \"exec\") {\n if (metadata.inputs) {\n result.inputs = Array.isArray(metadata.inputs)\n ? (metadata.inputs as string[])\n : [String(metadata.inputs)];\n }\n if (metadata.outputs) {\n result.outputs = Array.isArray(metadata.outputs)\n ? (metadata.outputs as string[])\n : [String(metadata.outputs)];\n }\n if (metadata.errors) {\n result.errors = Array.isArray(metadata.errors)\n ? (metadata.errors as string[])\n : [String(metadata.errors)];\n }\n if (metadata.sideEffects) {\n result.sideEffects = Array.isArray(metadata.sideEffects)\n ? (metadata.sideEffects as string[])\n : [String(metadata.sideEffects)];\n }\n }\n\n // Clean up empty metadata\n if (Object.keys(result.metadata!).length === 0) {\n delete result.metadata;\n }\n\n return result;\n}\n\nfunction explainOverview(): ExplainResult {\n return {\n topic: \"AFS Overview\",\n explanation: `AFS (Abstract File System) is a virtual filesystem that unifies different data sources into a single namespace.\n\nCore Concepts:\n- mount: Mount a data source to a virtual path\n- path: Virtual path, e.g., /src, /data\n- uri: Data source address, e.g., fs://, git://, sqlite://\n\nData Flow:\n User Path -> AFS -> /modules/{name} -> Provider -> Actual Data`,\n examples: [\n \"afs mount add /src fs:///path/to/source\",\n \"afs ls /modules/src\",\n \"afs read /modules/src/file.txt\",\n ],\n };\n}\n\nasync function explainMounts(cwd: string): Promise<ExplainResult> {\n const result = await mountListCommand(cwd);\n\n if (result.mounts.length === 0) {\n return {\n topic: \"Mounts\",\n explanation: `No mounts configured.\n\nUse mount add to add a mount:\n afs mount add <path> <uri>\n\npath: Virtual path for accessing data in AFS\nuri: Data source URI specifying the data origin`,\n examples: [\n \"afs mount add /src fs:///Users/me/project\",\n \"afs mount add /data sqlite:///data.db\",\n ],\n };\n }\n\n const mountList = result.mounts\n .map((m) => ` ${m.path} → ${m.uri}${m.description ? ` (${m.description})` : \"\"}`)\n .join(\"\\n\");\n\n return {\n topic: \"Mounts\",\n explanation: `Current mount configuration:\n\n${mountList}\n\nAfter mounting, data is accessed via /modules/{name}:\n path=\"/src\" -> /modules/src`,\n examples: result.mounts.map((m) => {\n const name = m.path.slice(1).replace(/\\//g, \"-\") || \"root\";\n return `afs ls /modules/${name}`;\n }),\n };\n}\n\nfunction explainPaths(): ExplainResult {\n return {\n topic: \"Paths\",\n explanation: `AFS Path Structure:\n\n/ Root directory\n/modules All mounted modules\n/modules/{name} Specific module, name derived from mount path\n\nPath Mapping:\n config: path=\"/src\" -> access: /modules/src\n config: path=\"/data\" -> access: /modules/data\n\nNote: The path configured by user is transformed to /modules/{name}`,\n };\n}\n\nfunction explainUri(): ExplainResult {\n return {\n topic: \"URI\",\n explanation: `AFS URI Schemes and Objects:\n\nfs:// Local Filesystem Provider\n Format: fs:///absolute/path or fs://./relative/path\n Objects:\n - directory: folder entries, supports list\n - file: file entries, supports read/write\n Operations: list, read, write, delete\n\ngit:// Git Repository Provider\n Format: git:///local/repo or git://github.com/user/repo?branch=main\n Objects:\n - directory: tree entries\n - file: blob entries, read-only\n Operations: list, read\n\nsqlite:// SQLite Database Provider\n Format: sqlite:///path/to/db.sqlite\n Objects:\n - directory: database root, tables list\n - exec: table entries, supports query operations\n Operations: list, read, exec (SQL queries)\n\njson:// JSON Data Provider\n Format: json:///path/to/data.json\n Objects:\n - directory: object/array entries\n - file: primitive values\n Operations: list, read, write`,\n examples: [\n \"afs mount add /src fs:///Users/me/project\",\n \"afs mount add /repo git://github.com/user/repo\",\n \"afs mount add /db sqlite:///data.sqlite\",\n ],\n };\n}\n\n/**\n * Format explain output for different views\n */\nexport function formatExplainOutput(result: ExplainResult, view: ViewType): string {\n switch (view) {\n case \"json\":\n return JSON.stringify(result, null, 2);\n case \"llm\":\n return formatLlm(result);\n default:\n return formatDefault(result);\n }\n}\n\nfunction formatDefault(result: ExplainResult): string {\n let output = `${result.topic}\\n${\"=\".repeat(result.topic.length)}\\n\\n${result.explanation}`;\n\n if (result.examples && result.examples.length > 0) {\n output += `\\n\\nExamples:\\n${result.examples.map((e) => ` $ ${e}`).join(\"\\n\")}`;\n }\n\n return output;\n}\n\nfunction formatLlm(result: ExplainResult): string {\n const lines = [`TOPIC ${result.topic}`, \"\", result.explanation];\n\n if (result.examples && result.examples.length > 0) {\n lines.push(\"\", \"EXAMPLES\");\n for (const example of result.examples) {\n lines.push(`CMD ${example}`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Format object explain output for different views\n */\nexport function formatPathExplainOutput(result: PathExplainResult, view: ViewType): string {\n switch (view) {\n case \"json\":\n return JSON.stringify(result, null, 2);\n case \"llm\":\n return formatPathLlm(result);\n default:\n return formatPathDefault(result);\n }\n}\n\nfunction formatPathDefault(result: PathExplainResult): string {\n const lines: string[] = [];\n\n lines.push(`PATH ${result.path}`);\n lines.push(\"\");\n lines.push(\"TYPE\");\n lines.push(result.type);\n\n if (result.description) {\n lines.push(\"\");\n lines.push(\"DESCRIPTION\");\n lines.push(result.description);\n }\n\n if (result.inputs && result.inputs.length > 0) {\n lines.push(\"\");\n lines.push(\"INPUTS\");\n for (const input of result.inputs) {\n lines.push(`- ${input}`);\n }\n }\n\n if (result.outputs && result.outputs.length > 0) {\n lines.push(\"\");\n lines.push(\"OUTPUTS\");\n for (const output of result.outputs) {\n lines.push(`- ${output}`);\n }\n }\n\n if (result.errors && result.errors.length > 0) {\n lines.push(\"\");\n lines.push(\"ERRORS\");\n for (const error of result.errors) {\n lines.push(`- ${error}`);\n }\n }\n\n if (result.sideEffects && result.sideEffects.length > 0) {\n lines.push(\"\");\n lines.push(\"SIDE EFFECTS\");\n for (const effect of result.sideEffects) {\n lines.push(`- ${effect}`);\n }\n } else {\n lines.push(\"\");\n lines.push(\"SIDE EFFECTS\");\n lines.push(\"- none\");\n }\n\n if (result.metadata && Object.keys(result.metadata).length > 0) {\n lines.push(\"\");\n lines.push(\"METADATA\");\n for (const [key, value] of Object.entries(result.metadata)) {\n lines.push(`- ${key}: ${value}`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction formatPathLlm(result: PathExplainResult): string {\n const lines: string[] = [];\n\n lines.push(`PATH ${result.path}`);\n lines.push(`TYPE ${result.type.toUpperCase()}`);\n\n if (result.description) {\n lines.push(`DESC ${result.description}`);\n }\n\n if (result.inputs && result.inputs.length > 0) {\n lines.push(`INPUTS ${result.inputs.join(\", \")}`);\n }\n\n if (result.outputs && result.outputs.length > 0) {\n lines.push(`OUTPUTS ${result.outputs.join(\", \")}`);\n }\n\n if (result.errors && result.errors.length > 0) {\n lines.push(`ERRORS ${result.errors.join(\", \")}`);\n }\n\n lines.push(`SIDE_EFFECTS ${result.sideEffects?.join(\", \") || \"none\"}`);\n\n if (result.metadata) {\n for (const [key, value] of Object.entries(result.metadata)) {\n lines.push(`${key.toUpperCase()} ${value}`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"explain.mjs","names":[],"sources":["../../src/commands/explain.ts"],"sourcesContent":["import type { AFSRuntime } from \"../runtime.js\";\nimport type { ViewType } from \"./ls.js\";\nimport { mountListCommand } from \"./mount.js\";\n\nexport interface ExplainResult {\n topic: string;\n explanation: string;\n examples?: string[];\n}\n\nexport interface PathExplainResult {\n path: string;\n type: string;\n description?: string;\n inputs?: string[];\n outputs?: string[];\n errors?: string[];\n sideEffects?: string[];\n metadata?: Record<string, string>;\n}\n\n/**\n * Explain AFS concepts or current configuration\n */\nexport async function explainCommand(cwd: string, topic?: string): Promise<ExplainResult> {\n switch (topic) {\n case \"mount\":\n case \"mounts\":\n return explainMounts(cwd);\n case \"path\":\n case \"paths\":\n return explainPaths();\n case \"uri\":\n return explainUri();\n default:\n return explainOverview();\n }\n}\n\n/**\n * Explain an AFS object at a given path\n */\nexport async function explainPathCommand(\n runtime: AFSRuntime,\n path: string,\n): Promise<PathExplainResult> {\n const readResult = await runtime.read(path);\n const entry = readResult.data;\n\n if (!entry) {\n return {\n path,\n type: \"unknown\",\n };\n }\n\n const metadata = entry.metadata || {};\n // Determine type from childrenCount: if defined, it has children (directory-like); otherwise file-like\n const entryType =\n metadata.kind === \"afs:executable\"\n ? \"exec\"\n : typeof metadata.childrenCount === \"number\"\n ? \"directory\"\n : \"file\";\n\n const result: PathExplainResult = {\n path: entry.path,\n type: entryType,\n description: metadata.description as string | undefined,\n metadata: {},\n };\n\n // Add metadata\n if (metadata.provider) {\n result.metadata!.provider = String(metadata.provider);\n }\n if (metadata.permissions) {\n const perms = metadata.permissions;\n result.metadata!.permissions = Array.isArray(perms) ? perms.join(\", \") : String(perms);\n }\n\n // For exec type, try to get schema info\n if (entryType === \"exec\") {\n if (metadata.inputs) {\n result.inputs = Array.isArray(metadata.inputs)\n ? (metadata.inputs as string[])\n : [String(metadata.inputs)];\n }\n if (metadata.outputs) {\n result.outputs = Array.isArray(metadata.outputs)\n ? (metadata.outputs as string[])\n : [String(metadata.outputs)];\n }\n if (metadata.errors) {\n result.errors = Array.isArray(metadata.errors)\n ? (metadata.errors as string[])\n : [String(metadata.errors)];\n }\n if (metadata.sideEffects) {\n result.sideEffects = Array.isArray(metadata.sideEffects)\n ? (metadata.sideEffects as string[])\n : [String(metadata.sideEffects)];\n }\n }\n\n // Clean up empty metadata\n if (Object.keys(result.metadata!).length === 0) {\n delete result.metadata;\n }\n\n return result;\n}\n\nfunction explainOverview(): ExplainResult {\n return {\n topic: \"AFS Overview\",\n explanation: `AFS (Abstract File System) is a virtual filesystem that unifies different data sources into a single namespace.\n\nCore Concepts:\n- mount: Mount a data source to a virtual path\n- path: Virtual path, e.g., /src, /data\n- uri: Data source address, e.g., fs://, git://, sqlite://\n\nData Flow:\n User Path -> AFS -> /modules/{name} -> Provider -> Actual Data`,\n examples: [\n \"afs mount add /src fs:///path/to/source\",\n \"afs ls /modules/src\",\n \"afs read /modules/src/file.txt\",\n ],\n };\n}\n\nasync function explainMounts(cwd: string): Promise<ExplainResult> {\n const result = await mountListCommand(cwd);\n\n if (result.mounts.length === 0) {\n return {\n topic: \"Mounts\",\n explanation: `No mounts configured.\n\nUse mount add to add a mount:\n afs mount add <path> <uri>\n\npath: Virtual path for accessing data in AFS\nuri: Data source URI specifying the data origin`,\n examples: [\n \"afs mount add /src fs:///Users/me/project\",\n \"afs mount add /data sqlite:///data.db\",\n ],\n };\n }\n\n const mountList = result.mounts\n .map((m) => ` ${m.path} → ${m.uri}${m.description ? ` (${m.description})` : \"\"}`)\n .join(\"\\n\");\n\n return {\n topic: \"Mounts\",\n explanation: `Current mount configuration:\n\n${mountList}\n\nAfter mounting, data is accessed via /modules/{name}:\n path=\"/src\" -> /modules/src`,\n examples: result.mounts.map((m) => {\n const name = m.path.slice(1).replace(/\\//g, \"-\") || \"root\";\n return `afs ls /modules/${name}`;\n }),\n };\n}\n\nfunction explainPaths(): ExplainResult {\n return {\n topic: \"Paths\",\n explanation: `AFS Path Structure:\n\n/ Root directory\n/modules All mounted modules\n/modules/{name} Specific module, name derived from mount path\n\nPath Mapping:\n config: path=\"/src\" -> access: /modules/src\n config: path=\"/data\" -> access: /modules/data\n\nNote: The path configured by user is transformed to /modules/{name}`,\n };\n}\n\nfunction explainUri(): ExplainResult {\n return {\n topic: \"URI\",\n explanation: `AFS URI Schemes and Objects:\n\nfs:// Local Filesystem Provider\n Format: fs:///absolute/path or fs://./relative/path\n Objects:\n - directory: folder entries, supports list\n - file: file entries, supports read/write\n Operations: list, read, write, delete\n\ngit:// Git Repository Provider\n Format: git:///local/repo or git://github.com/user/repo?branch=main\n Objects:\n - directory: tree entries\n - file: blob entries, read-only\n Operations: list, read\n\nsqlite:// SQLite Database Provider\n Format: sqlite:///path/to/db.sqlite\n Objects:\n - directory: database root, tables list\n - exec: table entries, supports query operations\n Operations: list, read, exec (SQL queries)\n\njson:// JSON Data Provider\n Format: json:///path/to/data.json\n Objects:\n - directory: object/array entries\n - file: primitive values\n Operations: list, read, write`,\n examples: [\n \"afs mount add /src fs:///Users/me/project\",\n \"afs mount add /repo git://github.com/user/repo\",\n \"afs mount add /db sqlite:///data.sqlite\",\n ],\n };\n}\n\n/**\n * Format explain output for different views\n */\nexport function formatExplainOutput(result: ExplainResult, view: ViewType): string {\n switch (view) {\n case \"json\":\n return JSON.stringify(result, null, 2);\n case \"llm\":\n return formatLlm(result);\n default:\n return formatDefault(result);\n }\n}\n\nfunction formatDefault(result: ExplainResult): string {\n let output = `${result.topic}\\n${\"=\".repeat(result.topic.length)}\\n\\n${result.explanation}`;\n\n if (result.examples && result.examples.length > 0) {\n output += `\\n\\nExamples:\\n${result.examples.map((e) => ` $ ${e}`).join(\"\\n\")}`;\n }\n\n return output;\n}\n\nfunction formatLlm(result: ExplainResult): string {\n const lines = [`TOPIC ${result.topic}`, \"\", result.explanation];\n\n if (result.examples && result.examples.length > 0) {\n lines.push(\"\", \"EXAMPLES\");\n for (const example of result.examples) {\n lines.push(`CMD ${example}`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Format object explain output for different views\n */\nexport function formatPathExplainOutput(result: PathExplainResult, view: ViewType): string {\n switch (view) {\n case \"json\":\n return JSON.stringify(result, null, 2);\n case \"llm\":\n return formatPathLlm(result);\n default:\n return formatPathDefault(result);\n }\n}\n\nfunction formatPathDefault(result: PathExplainResult): string {\n const lines: string[] = [];\n\n lines.push(`PATH ${result.path}`);\n lines.push(\"\");\n lines.push(\"TYPE\");\n lines.push(result.type);\n\n if (result.description) {\n lines.push(\"\");\n lines.push(\"DESCRIPTION\");\n lines.push(result.description);\n }\n\n if (result.inputs && result.inputs.length > 0) {\n lines.push(\"\");\n lines.push(\"INPUTS\");\n for (const input of result.inputs) {\n lines.push(`- ${input}`);\n }\n }\n\n if (result.outputs && result.outputs.length > 0) {\n lines.push(\"\");\n lines.push(\"OUTPUTS\");\n for (const output of result.outputs) {\n lines.push(`- ${output}`);\n }\n }\n\n if (result.errors && result.errors.length > 0) {\n lines.push(\"\");\n lines.push(\"ERRORS\");\n for (const error of result.errors) {\n lines.push(`- ${error}`);\n }\n }\n\n if (result.sideEffects && result.sideEffects.length > 0) {\n lines.push(\"\");\n lines.push(\"SIDE EFFECTS\");\n for (const effect of result.sideEffects) {\n lines.push(`- ${effect}`);\n }\n } else {\n lines.push(\"\");\n lines.push(\"SIDE EFFECTS\");\n lines.push(\"- none\");\n }\n\n if (result.metadata && Object.keys(result.metadata).length > 0) {\n lines.push(\"\");\n lines.push(\"METADATA\");\n for (const [key, value] of Object.entries(result.metadata)) {\n lines.push(`- ${key}: ${value}`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction formatPathLlm(result: PathExplainResult): string {\n const lines: string[] = [];\n\n lines.push(`PATH ${result.path}`);\n lines.push(`TYPE ${result.type.toUpperCase()}`);\n\n if (result.description) {\n lines.push(`DESC ${result.description}`);\n }\n\n if (result.inputs && result.inputs.length > 0) {\n lines.push(`INPUTS ${result.inputs.join(\", \")}`);\n }\n\n if (result.outputs && result.outputs.length > 0) {\n lines.push(`OUTPUTS ${result.outputs.join(\", \")}`);\n }\n\n if (result.errors && result.errors.length > 0) {\n lines.push(`ERRORS ${result.errors.join(\", \")}`);\n }\n\n lines.push(`SIDE_EFFECTS ${result.sideEffects?.join(\", \") || \"none\"}`);\n\n if (result.metadata) {\n for (const [key, value] of Object.entries(result.metadata)) {\n lines.push(`${key.toUpperCase()} ${value}`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n"],"mappings":"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"}
|
package/dist/commands/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { execCommand, formatExecOutput } from "./exec.mjs";
|
|
1
|
+
import { execCommand, formatExecOutput, isExecutable, parseExecArgsWithStdin } from "./exec.mjs";
|
|
2
2
|
import { formatMountListOutput, mountAddCommand, mountListCommand, mountRemoveCommand, mountValidateCommand } from "./mount.mjs";
|
|
3
3
|
import { explainCommand, explainPathCommand, formatExplainOutput, formatPathExplainOutput } from "./explain.mjs";
|
|
4
4
|
import { formatLsOutput, lsCommand } from "./ls.mjs";
|
package/dist/commands/ls.cjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const require_index = require('../ui/index.cjs');
|
|
2
|
+
const require_meta = require('../utils/meta.cjs');
|
|
2
3
|
|
|
3
4
|
//#region src/commands/ls.ts
|
|
4
5
|
/**
|
|
@@ -11,15 +12,18 @@ async function lsCommand(runtime, path, options = {}) {
|
|
|
11
12
|
maxChildren: options.maxChildren,
|
|
12
13
|
pattern: options.pattern
|
|
13
14
|
});
|
|
14
|
-
const entries = result.data.map((entry) =>
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
const entries = result.data.map((entry) => {
|
|
16
|
+
const lsEntry = {
|
|
17
|
+
path: entry.path,
|
|
18
|
+
size: entry.metadata?.size,
|
|
19
|
+
modified: formatDate(entry.updatedAt),
|
|
20
|
+
childrenCount: entry.metadata?.childrenCount
|
|
21
|
+
};
|
|
22
|
+
const meta = extractMeta(entry.metadata);
|
|
23
|
+
if (meta && Object.keys(meta).length > 0) lsEntry.meta = meta;
|
|
24
|
+
return lsEntry;
|
|
25
|
+
});
|
|
26
|
+
const truncated = options.limit !== void 0 && entries.length >= options.limit || result.message?.toLowerCase().includes("truncat");
|
|
23
27
|
return {
|
|
24
28
|
entries,
|
|
25
29
|
total: entries.length,
|
|
@@ -27,9 +31,20 @@ async function lsCommand(runtime, path, options = {}) {
|
|
|
27
31
|
message: result.message
|
|
28
32
|
};
|
|
29
33
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Extract meta fields from entry metadata
|
|
36
|
+
*/
|
|
37
|
+
function extractMeta(metadata) {
|
|
38
|
+
if (!metadata) return void 0;
|
|
39
|
+
const builtInFields = new Set([
|
|
40
|
+
"type",
|
|
41
|
+
"size",
|
|
42
|
+
"mimeType",
|
|
43
|
+
"childrenCount"
|
|
44
|
+
]);
|
|
45
|
+
const meta = {};
|
|
46
|
+
for (const [key, value] of Object.entries(metadata)) if (!builtInFields.has(key) && value !== void 0) meta[key] = value;
|
|
47
|
+
return Object.keys(meta).length > 0 ? meta : void 0;
|
|
33
48
|
}
|
|
34
49
|
function formatDate(date) {
|
|
35
50
|
if (!date) return void 0;
|
|
@@ -39,18 +54,20 @@ function formatDate(date) {
|
|
|
39
54
|
/**
|
|
40
55
|
* Format ls output for different views
|
|
41
56
|
*/
|
|
42
|
-
function formatLsOutput(result, view) {
|
|
57
|
+
function formatLsOutput(result, view, options) {
|
|
58
|
+
const isActions = options?.path && require_meta.isActionsPath(options.path);
|
|
43
59
|
switch (view) {
|
|
44
|
-
case "json": return formatJson(result);
|
|
45
|
-
case "llm": return formatLlm(result);
|
|
46
|
-
case "human": return formatHuman(result);
|
|
47
|
-
default: return formatDefault(result);
|
|
60
|
+
case "json": return isActions ? formatActionsJson(result, options.path) : formatJson(result);
|
|
61
|
+
case "llm": return isActions ? formatActionsLlm(result, options.path) : formatLlm(result);
|
|
62
|
+
case "human": return isActions ? formatActionsHuman(result, options.path) : formatHuman(result);
|
|
63
|
+
default: return isActions ? formatActionsDefault(result) : formatDefault(result);
|
|
48
64
|
}
|
|
49
65
|
}
|
|
50
66
|
/**
|
|
51
67
|
* Default format: Machine truth, one path per line
|
|
52
68
|
*/
|
|
53
69
|
function formatDefault(result) {
|
|
70
|
+
if (result.entries.length === 0 && result.message) return `# ${result.message}`;
|
|
54
71
|
const lines = result.entries.map((entry) => entry.path);
|
|
55
72
|
if (result.truncated) lines.push(`# Results truncated (${result.total} shown)`);
|
|
56
73
|
return lines.join("\n");
|
|
@@ -67,16 +84,19 @@ function formatJson(result) {
|
|
|
67
84
|
}, null, 2);
|
|
68
85
|
}
|
|
69
86
|
/**
|
|
70
|
-
* LLM format: Token-efficient, semantic facts
|
|
87
|
+
* LLM format: Token-efficient, semantic facts (includes KIND if present)
|
|
71
88
|
*/
|
|
72
89
|
function formatLlm(result) {
|
|
73
90
|
const lines = [];
|
|
91
|
+
if (result.entries.length === 0 && result.message) {
|
|
92
|
+
lines.push(`ERROR ${result.message}`);
|
|
93
|
+
return lines.join("\n");
|
|
94
|
+
}
|
|
74
95
|
for (const entry of result.entries) {
|
|
75
96
|
const parts = [`ENTRY ${entry.path}`];
|
|
76
|
-
parts.push(`
|
|
97
|
+
if (entry.meta?.kind) parts.push(`KIND=${entry.meta.kind}`);
|
|
77
98
|
if (entry.size !== void 0) parts.push(`SIZE=${entry.size}`);
|
|
78
99
|
if (entry.childrenCount !== void 0) parts.push(`CHILDREN=${entry.childrenCount}`);
|
|
79
|
-
if (entry.childrenTruncated) parts.push("TRUNCATED");
|
|
80
100
|
lines.push(parts.join(" "));
|
|
81
101
|
}
|
|
82
102
|
lines.push(`TOTAL ${result.total}`);
|
|
@@ -87,6 +107,7 @@ function formatLlm(result) {
|
|
|
87
107
|
* Human format: Tree structure
|
|
88
108
|
*/
|
|
89
109
|
function formatHuman(result) {
|
|
110
|
+
if (result.entries.length === 0 && result.message) return require_index.colors.yellow(result.message);
|
|
90
111
|
const root = {
|
|
91
112
|
name: "",
|
|
92
113
|
children: /* @__PURE__ */ new Map()
|
|
@@ -118,25 +139,103 @@ function renderTree(node, prefix, lines) {
|
|
|
118
139
|
const child = children[i];
|
|
119
140
|
const isLast = i === children.length - 1;
|
|
120
141
|
const connector = require_index.colors.dim(isLast ? "└── " : "├── ");
|
|
121
|
-
const icon = child.entry
|
|
122
|
-
const name = child.entry?.type === "directory" ? require_index.colors.cyan(child.name) : child.name;
|
|
142
|
+
const icon = typeof child.entry?.childrenCount === "number" || child.children.size > 0 ? "📁" : "📄";
|
|
123
143
|
const sizeStr = child.entry?.size !== void 0 ? require_index.colors.dim(` ${formatSize(child.entry.size)}`) : "";
|
|
124
|
-
|
|
144
|
+
const kindStr = child.entry?.meta?.kind ? require_index.colors.dim(` (${child.entry.meta.kind})`) : "";
|
|
145
|
+
lines.push(`${require_index.colors.dim(prefix)}${connector}${icon} ${child.name}${kindStr}${sizeStr}`);
|
|
125
146
|
renderTree(child, prefix + (isLast ? " " : "│ "), lines);
|
|
126
147
|
}
|
|
127
148
|
}
|
|
128
|
-
function getTypeIcon(type) {
|
|
129
|
-
switch (type) {
|
|
130
|
-
case "directory": return "📂";
|
|
131
|
-
case "file": return "📄";
|
|
132
|
-
default: return "📄";
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
149
|
function formatSize(bytes) {
|
|
136
150
|
if (bytes < 1024) return `${bytes}B`;
|
|
137
151
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
138
152
|
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
139
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Extract action name from path (last segment after .actions/)
|
|
156
|
+
*/
|
|
157
|
+
function getActionName(entry) {
|
|
158
|
+
const parts = entry.path.split("/");
|
|
159
|
+
return parts[parts.length - 1] || entry.path;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Get action description from entry metadata
|
|
163
|
+
*/
|
|
164
|
+
function getActionDescription(entry) {
|
|
165
|
+
return entry.meta?.description;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Get the node path from an actions path (path before /.actions)
|
|
169
|
+
*/
|
|
170
|
+
function getNodePathFromActions(actionsPath) {
|
|
171
|
+
const idx = actionsPath.lastIndexOf("/.actions");
|
|
172
|
+
return idx > 0 ? actionsPath.substring(0, idx) : "/";
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Format actions list - Human view
|
|
176
|
+
* Shows: Available actions for /path:
|
|
177
|
+
* export Export table data to file
|
|
178
|
+
* truncate Delete all rows from table
|
|
179
|
+
*/
|
|
180
|
+
function formatActionsHuman(result, path) {
|
|
181
|
+
const lines = [];
|
|
182
|
+
const nodePath = getNodePathFromActions(path);
|
|
183
|
+
if (result.entries.length === 0) {
|
|
184
|
+
lines.push(`No actions available for ${nodePath}`);
|
|
185
|
+
return lines.join("\n");
|
|
186
|
+
}
|
|
187
|
+
lines.push(`Available actions for ${nodePath}:`);
|
|
188
|
+
lines.push("");
|
|
189
|
+
const maxNameLen = Math.max(...result.entries.map((e) => getActionName(e).length));
|
|
190
|
+
for (const entry of result.entries) {
|
|
191
|
+
const name = getActionName(entry);
|
|
192
|
+
const desc = getActionDescription(entry) || "";
|
|
193
|
+
lines.push(` ${name.padEnd(maxNameLen + 2)}${desc}`);
|
|
194
|
+
}
|
|
195
|
+
return lines.join("\n");
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Format actions list - LLM view
|
|
199
|
+
* Shows: ACTIONS /path/.actions
|
|
200
|
+
* ACTION export DESCRIPTION "Export table data to file"
|
|
201
|
+
*/
|
|
202
|
+
function formatActionsLlm(result, path) {
|
|
203
|
+
const lines = [];
|
|
204
|
+
lines.push(`ACTIONS ${path}`);
|
|
205
|
+
if (result.entries.length === 0) {
|
|
206
|
+
lines.push("ACTIONS_COUNT 0");
|
|
207
|
+
return lines.join("\n");
|
|
208
|
+
}
|
|
209
|
+
for (const entry of result.entries) {
|
|
210
|
+
const name = getActionName(entry);
|
|
211
|
+
const desc = getActionDescription(entry);
|
|
212
|
+
const descPart = desc ? ` DESCRIPTION "${desc}"` : "";
|
|
213
|
+
lines.push(`ACTION ${name}${descPart}`);
|
|
214
|
+
}
|
|
215
|
+
return lines.join("\n");
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Format actions list - JSON view
|
|
219
|
+
* Shows structured JSON with path and actions array
|
|
220
|
+
*/
|
|
221
|
+
function formatActionsJson(result, path) {
|
|
222
|
+
const actions = result.entries.map((entry) => ({
|
|
223
|
+
name: getActionName(entry),
|
|
224
|
+
description: getActionDescription(entry)
|
|
225
|
+
}));
|
|
226
|
+
return JSON.stringify({
|
|
227
|
+
path,
|
|
228
|
+
data: actions
|
|
229
|
+
}, null, 2);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Format actions list - Default view
|
|
233
|
+
* Shows action names, one per line
|
|
234
|
+
*/
|
|
235
|
+
function formatActionsDefault(result) {
|
|
236
|
+
if (result.entries.length === 0) return "";
|
|
237
|
+
return result.entries.map((entry) => getActionName(entry)).join("\n");
|
|
238
|
+
}
|
|
140
239
|
|
|
141
240
|
//#endregion
|
|
142
241
|
exports.formatLsOutput = formatLsOutput;
|