@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/dist/commands/read.mjs
CHANGED
|
@@ -1,21 +1,91 @@
|
|
|
1
|
+
import { isActionsPath, isMetaPath } from "../utils/meta.mjs";
|
|
2
|
+
import { AFSNotFoundError } from "@aigne/afs";
|
|
3
|
+
|
|
1
4
|
//#region src/commands/read.ts
|
|
2
5
|
/**
|
|
3
6
|
* Read file content
|
|
4
7
|
*/
|
|
5
8
|
async function readCommand(runtime, path) {
|
|
6
|
-
|
|
9
|
+
let result;
|
|
10
|
+
try {
|
|
11
|
+
result = await runtime.read(path);
|
|
12
|
+
} catch (error) {
|
|
13
|
+
if (error instanceof AFSNotFoundError) return {
|
|
14
|
+
path,
|
|
15
|
+
content: void 0
|
|
16
|
+
};
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
7
19
|
if (!result.data) return {
|
|
8
20
|
path,
|
|
9
21
|
content: void 0
|
|
10
22
|
};
|
|
11
23
|
const entry = result.data;
|
|
12
|
-
|
|
24
|
+
const isMeta = isMetaPath(path);
|
|
25
|
+
const isAction = isActionsPath(path);
|
|
26
|
+
const content = isMeta ? entry.content : typeof entry.content === "string" ? entry.content : entry.content !== void 0 ? JSON.stringify(entry.content) : void 0;
|
|
27
|
+
const readResult = {
|
|
13
28
|
path: entry.path,
|
|
14
|
-
content
|
|
15
|
-
|
|
29
|
+
content,
|
|
30
|
+
isMeta,
|
|
31
|
+
isAction,
|
|
16
32
|
size: entry.metadata?.size,
|
|
17
|
-
mimeType: entry.metadata?.mimeType
|
|
33
|
+
mimeType: entry.metadata?.mimeType,
|
|
34
|
+
childrenCount: entry.metadata?.childrenCount
|
|
18
35
|
};
|
|
36
|
+
if (!isMeta && !isAction) try {
|
|
37
|
+
const statResult = await runtime.stat(path);
|
|
38
|
+
if (statResult.data?.actions && statResult.data.actions.length > 0) readResult.actions = statResult.data.actions;
|
|
39
|
+
} catch {}
|
|
40
|
+
const meta = extractMeta(entry.metadata);
|
|
41
|
+
if (meta && Object.keys(meta).length > 0) readResult.meta = meta;
|
|
42
|
+
return readResult;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Extract meta fields from entry metadata
|
|
46
|
+
* Excludes built-in fields that are exposed separately
|
|
47
|
+
*/
|
|
48
|
+
function extractMeta(metadata) {
|
|
49
|
+
if (!metadata) return void 0;
|
|
50
|
+
const builtInFields = new Set([
|
|
51
|
+
"type",
|
|
52
|
+
"size",
|
|
53
|
+
"mimeType",
|
|
54
|
+
"childrenCount"
|
|
55
|
+
]);
|
|
56
|
+
const meta = {};
|
|
57
|
+
for (const [key, value] of Object.entries(metadata)) if (!builtInFields.has(key) && value !== void 0) meta[key] = value;
|
|
58
|
+
return Object.keys(meta).length > 0 ? meta : void 0;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Extract user-defined metadata (excludes system fields)
|
|
62
|
+
*/
|
|
63
|
+
function extractUserMetadata(meta) {
|
|
64
|
+
if (!meta) return {};
|
|
65
|
+
const systemFields = new Set([
|
|
66
|
+
"kind",
|
|
67
|
+
"kinds",
|
|
68
|
+
"childrenCount",
|
|
69
|
+
"inputSchema",
|
|
70
|
+
"required",
|
|
71
|
+
"description"
|
|
72
|
+
]);
|
|
73
|
+
return Object.fromEntries(Object.entries(meta).filter(([k]) => !systemFields.has(k)));
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Format params summary: format*,path,limit (* = required)
|
|
77
|
+
*/
|
|
78
|
+
function formatParamsSummary(schema, required = []) {
|
|
79
|
+
if (!schema) return "";
|
|
80
|
+
const props = schema.properties || schema;
|
|
81
|
+
return Object.keys(props).map((name) => required.includes(name) ? `${name}*` : name).join(",");
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Check if entry is executable (has afs:executable in kinds)
|
|
85
|
+
*/
|
|
86
|
+
function isExecutable(meta) {
|
|
87
|
+
if (!meta?.kinds) return false;
|
|
88
|
+
return meta.kinds.includes("afs:executable");
|
|
19
89
|
}
|
|
20
90
|
/**
|
|
21
91
|
* Format read output for different views
|
|
@@ -29,34 +99,163 @@ function formatReadOutput(read, view) {
|
|
|
29
99
|
}
|
|
30
100
|
}
|
|
31
101
|
/**
|
|
32
|
-
* Default format: Raw content
|
|
102
|
+
* Default format: Raw content or one-liner summary
|
|
103
|
+
* - If content exists: show raw content
|
|
104
|
+
* - If no content (directories): show one-liner summary /path kind children=N actions=a,b,c
|
|
105
|
+
* - For executables: /path afs:executable params=format*,path,limit
|
|
106
|
+
* - For meta paths: raw JSON
|
|
33
107
|
*/
|
|
34
108
|
function formatDefault(read) {
|
|
35
|
-
|
|
109
|
+
if (read.isMeta && typeof read.content === "object") return JSON.stringify(read.content, null, 2);
|
|
110
|
+
if (read.isAction && isExecutable(read.meta)) {
|
|
111
|
+
if (read.content !== void 0 && read.content !== null && read.content !== "") return typeof read.content === "string" ? read.content : JSON.stringify(read.content, null, 2);
|
|
112
|
+
const parts$1 = [read.path, "afs:executable"];
|
|
113
|
+
if (read.meta?.inputSchema) {
|
|
114
|
+
const params = formatParamsSummary(read.meta.inputSchema, read.meta.required);
|
|
115
|
+
if (params) parts$1.push(`params=${params}`);
|
|
116
|
+
}
|
|
117
|
+
return parts$1.join(" ");
|
|
118
|
+
}
|
|
119
|
+
if (read.content !== void 0 && read.content !== null && read.content !== "") return typeof read.content === "string" ? read.content : JSON.stringify(read.content, null, 2);
|
|
120
|
+
const parts = [read.path];
|
|
121
|
+
if (read.meta?.kind) parts.push(read.meta.kind);
|
|
122
|
+
if (read.childrenCount !== void 0) parts.push(`children=${read.childrenCount}`);
|
|
123
|
+
if (read.actions && read.actions.length > 0) parts.push(`actions=${read.actions.map((a) => a.name).join(",")}`);
|
|
124
|
+
return parts.join(" ");
|
|
36
125
|
}
|
|
37
126
|
/**
|
|
38
|
-
* JSON format
|
|
127
|
+
* JSON format (includes meta field for regular files if present)
|
|
39
128
|
*/
|
|
40
129
|
function formatJson(read) {
|
|
41
130
|
return JSON.stringify(read, null, 2);
|
|
42
131
|
}
|
|
43
132
|
/**
|
|
44
|
-
* LLM format:
|
|
133
|
+
* LLM format: Structured text for AI parsing
|
|
45
134
|
*/
|
|
46
135
|
function formatLlm(read) {
|
|
47
136
|
const lines = [];
|
|
48
|
-
|
|
137
|
+
if (read.isMeta) {
|
|
138
|
+
lines.push(`META ${read.path}`);
|
|
139
|
+
const content = read.content;
|
|
140
|
+
if (content?.kind) lines.push(`KIND ${content.kind}`);
|
|
141
|
+
lines.push(`CONTENT`);
|
|
142
|
+
lines.push(JSON.stringify(content));
|
|
143
|
+
return lines.join("\n");
|
|
144
|
+
}
|
|
145
|
+
if (read.isAction && isExecutable(read.meta)) {
|
|
146
|
+
lines.push(`EXECUTABLE ${read.path}`);
|
|
147
|
+
if (read.meta?.description) lines.push(`DESCRIPTION "${read.meta.description}"`);
|
|
148
|
+
if (read.meta?.inputSchema) {
|
|
149
|
+
const props = read.meta.inputSchema.properties || {};
|
|
150
|
+
const required = read.meta.required || [];
|
|
151
|
+
for (const [name, propDef] of Object.entries(props)) {
|
|
152
|
+
const prop = propDef;
|
|
153
|
+
const isReq = required.includes(name);
|
|
154
|
+
let paramLine = `PARAM ${name} TYPE ${prop.type || "any"}`;
|
|
155
|
+
paramLine += isReq ? " REQUIRED" : " OPTIONAL";
|
|
156
|
+
if (prop.enum) paramLine += ` CHOICES ${prop.enum.join(",")}`;
|
|
157
|
+
if (prop.default !== void 0) paramLine += ` DEFAULT ${prop.default}`;
|
|
158
|
+
lines.push(paramLine);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return lines.join("\n");
|
|
162
|
+
}
|
|
163
|
+
lines.push(`NODE ${read.path}`);
|
|
164
|
+
if (read.meta?.kind) lines.push(`KIND ${read.meta.kind}`);
|
|
165
|
+
if (read.meta?.kinds && Array.isArray(read.meta.kinds) && read.meta.kinds.length > 0) lines.push(`KINDS ${read.meta.kinds.join(" ")}`);
|
|
166
|
+
if (read.childrenCount !== void 0) lines.push(`CHILDREN ${read.childrenCount}`);
|
|
49
167
|
if (read.mimeType) lines.push(`MIME ${read.mimeType}`);
|
|
50
168
|
if (read.size !== void 0) lines.push(`SIZE ${read.size}`);
|
|
51
|
-
|
|
52
|
-
|
|
169
|
+
const userMeta = extractUserMetadata(read.meta);
|
|
170
|
+
if (Object.keys(userMeta).length > 0) {
|
|
171
|
+
const metaStr = Object.entries(userMeta).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(" ");
|
|
172
|
+
lines.push(`META ${metaStr}`);
|
|
173
|
+
}
|
|
174
|
+
if (read.actions && read.actions.length > 0) lines.push(`ACTIONS ${read.actions.map((a) => a.name).join(" ")}`);
|
|
175
|
+
if (read.content !== void 0 && read.content !== null && read.content !== "") lines.push(`CONTENT ${JSON.stringify(read.content)}`);
|
|
53
176
|
return lines.join("\n");
|
|
54
177
|
}
|
|
55
178
|
/**
|
|
56
|
-
* Human format:
|
|
179
|
+
* Human format: Formatted display with metadata and actions
|
|
57
180
|
*/
|
|
58
181
|
function formatHuman(read) {
|
|
59
|
-
|
|
182
|
+
const lines = [];
|
|
183
|
+
if (read.isMeta && typeof read.content === "object") {
|
|
184
|
+
lines.push(`Metadata for ${read.path.replace(/\/.meta$/, "")}`);
|
|
185
|
+
lines.push("");
|
|
186
|
+
lines.push(JSON.stringify(read.content, null, 2));
|
|
187
|
+
return lines.join("\n");
|
|
188
|
+
}
|
|
189
|
+
if (read.isAction && isExecutable(read.meta)) {
|
|
190
|
+
const actionName = read.path.split("/").pop() || "action";
|
|
191
|
+
lines.push(`Action: ${actionName}`);
|
|
192
|
+
lines.push(`Path: ${read.path}`);
|
|
193
|
+
if (read.meta?.description) {
|
|
194
|
+
lines.push("");
|
|
195
|
+
lines.push("Description:");
|
|
196
|
+
lines.push(` ${read.meta.description}`);
|
|
197
|
+
}
|
|
198
|
+
if (read.meta?.inputSchema) {
|
|
199
|
+
const props = read.meta.inputSchema.properties || {};
|
|
200
|
+
const required = read.meta.required || [];
|
|
201
|
+
if (Object.keys(props).length > 0) {
|
|
202
|
+
lines.push("");
|
|
203
|
+
lines.push("Parameters:");
|
|
204
|
+
for (const [name, propDef] of Object.entries(props)) {
|
|
205
|
+
const prop = propDef;
|
|
206
|
+
const isReq = required.includes(name);
|
|
207
|
+
const typeStr = `<${prop.type || "any"}>`;
|
|
208
|
+
const reqLabel = isReq ? "(required)" : "(optional)";
|
|
209
|
+
let line = ` --${name} ${typeStr}`.padEnd(24);
|
|
210
|
+
line += prop.description || "";
|
|
211
|
+
line += ` ${reqLabel}`;
|
|
212
|
+
lines.push(line);
|
|
213
|
+
if (prop.enum) lines.push(`${"".padEnd(24)}Choices: ${prop.enum.join(", ")}`);
|
|
214
|
+
if (prop.default !== void 0) lines.push(`${"".padEnd(24)}Default: ${prop.default}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
lines.push("");
|
|
219
|
+
lines.push("Usage:");
|
|
220
|
+
lines.push(` afs exec ${read.path}`);
|
|
221
|
+
return lines.join("\n");
|
|
222
|
+
}
|
|
223
|
+
lines.push(read.path);
|
|
224
|
+
lines.push("");
|
|
225
|
+
if (read.meta?.kind) lines.push(`Kind: ${read.meta.kind}`);
|
|
226
|
+
if (read.meta?.kinds && Array.isArray(read.meta.kinds) && read.meta.kinds.length > 0) lines.push(`Kinds: ${read.meta.kinds.join(" → ")}`);
|
|
227
|
+
if (read.childrenCount !== void 0) lines.push(`Children: ${read.childrenCount}`);
|
|
228
|
+
const userMeta = extractUserMetadata(read.meta);
|
|
229
|
+
if (Object.keys(userMeta).length > 0) {
|
|
230
|
+
lines.push("");
|
|
231
|
+
lines.push("Metadata:");
|
|
232
|
+
for (const [key, value] of Object.entries(userMeta)) lines.push(` ${key.padEnd(12)} ${formatValue(value)}`);
|
|
233
|
+
}
|
|
234
|
+
if (read.actions && read.actions.length > 0) {
|
|
235
|
+
lines.push("");
|
|
236
|
+
lines.push(`Actions: ${read.actions.map((a) => a.name).join(", ")}`);
|
|
237
|
+
}
|
|
238
|
+
if (read.content !== void 0 && read.content !== null && read.content !== "") {
|
|
239
|
+
lines.push("");
|
|
240
|
+
lines.push("Content:");
|
|
241
|
+
lines.push(formatContent(read.content));
|
|
242
|
+
} else if (read.childrenCount !== void 0 && read.childrenCount > 0) {
|
|
243
|
+
lines.push("");
|
|
244
|
+
lines.push("Content:");
|
|
245
|
+
lines.push(" (directory - use 'afs ls' to list children)");
|
|
246
|
+
}
|
|
247
|
+
return lines.join("\n");
|
|
248
|
+
}
|
|
249
|
+
function formatValue(value) {
|
|
250
|
+
if (value === null || value === void 0) return "";
|
|
251
|
+
if (typeof value === "string") return value;
|
|
252
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
253
|
+
if (Array.isArray(value)) return value.join(", ");
|
|
254
|
+
return JSON.stringify(value);
|
|
255
|
+
}
|
|
256
|
+
function formatContent(content) {
|
|
257
|
+
if (typeof content === "string") return content.split("\n").map((line) => ` ${line}`).join("\n");
|
|
258
|
+
return ` ${JSON.stringify(content, null, 2).split("\n").join("\n ")}`;
|
|
60
259
|
}
|
|
61
260
|
|
|
62
261
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read.mjs","names":[],"sources":["../../src/commands/read.ts"],"sourcesContent":["import type { AFSRuntime } from \"../runtime.js\";\nimport type { ViewType } from \"./ls.js\";\n\nexport interface ReadResult {\n path: string;\n content?: string;\n type?: \"file\" | \"directory\";\n size?: number;\n mimeType?: string;\n}\n\n/**\n * Read file content\n */\nexport async function readCommand(runtime: AFSRuntime, path: string): Promise<ReadResult> {\n const result = await runtime.read(path);\n\n if (!result.data) {\n return {\n path,\n content: undefined,\n };\n }\n\n const entry = result.data;\n return {\n path: entry.path,\n content: typeof entry.content === \"string\" ? entry.content : JSON.stringify(entry.content),\n type: entry.metadata?.type === \"directory\" ? \"directory\" : \"file\",\n size: entry.metadata?.size,\n mimeType: entry.metadata?.mimeType,\n };\n}\n\n/**\n * Format read output for different views\n */\nexport function formatReadOutput(read: ReadResult, view: ViewType): string {\n switch (view) {\n case \"json\":\n return formatJson(read);\n case \"llm\":\n return formatLlm(read);\n case \"human\":\n return formatHuman(read);\n default:\n return formatDefault(read);\n }\n}\n\n/**\n * Default format: Raw content\n */\nfunction formatDefault(read: ReadResult): string {\n return read.content ?? \"\";\n}\n\n/**\n * JSON format\n */\nfunction formatJson(read: ReadResult): string {\n return JSON.stringify(read, null, 2);\n}\n\n/**\n * LLM format: Content with header\n */\nfunction formatLlm(read: ReadResult): string {\n const lines: string[] = [];\n\n lines.push(`FILE ${read.path}`);\n\n if (read.mimeType) {\n lines.push(`MIME ${read.mimeType}`);\n }\n\n if (read.size !== undefined) {\n lines.push(`SIZE ${read.size}`);\n }\n\n lines.push(`CONTENT`);\n lines.push(read.content ?? \"\");\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Human format: With syntax highlighting placeholder\n */\nfunction formatHuman(read: ReadResult): string {\n const header = `--- ${read.path} ---`;\n return `${header}\\n${read.content ?? \"(empty)\"}`;\n}\n"],"mappings":";;;;AAcA,eAAsB,YAAY,SAAqB,MAAmC;CACxF,MAAM,SAAS,MAAM,QAAQ,KAAK,KAAK;AAEvC,KAAI,CAAC,OAAO,KACV,QAAO;EACL;EACA,SAAS;EACV;CAGH,MAAM,QAAQ,OAAO;AACrB,QAAO;EACL,MAAM,MAAM;EACZ,SAAS,OAAO,MAAM,YAAY,WAAW,MAAM,UAAU,KAAK,UAAU,MAAM,QAAQ;EAC1F,MAAM,MAAM,UAAU,SAAS,cAAc,cAAc;EAC3D,MAAM,MAAM,UAAU;EACtB,UAAU,MAAM,UAAU;EAC3B;;;;;AAMH,SAAgB,iBAAiB,MAAkB,MAAwB;AACzE,SAAQ,MAAR;EACE,KAAK,OACH,QAAO,WAAW,KAAK;EACzB,KAAK,MACH,QAAO,UAAU,KAAK;EACxB,KAAK,QACH,QAAO,YAAY,KAAK;EAC1B,QACE,QAAO,cAAc,KAAK;;;;;;AAOhC,SAAS,cAAc,MAA0B;AAC/C,QAAO,KAAK,WAAW;;;;;AAMzB,SAAS,WAAW,MAA0B;AAC5C,QAAO,KAAK,UAAU,MAAM,MAAM,EAAE;;;;;AAMtC,SAAS,UAAU,MAA0B;CAC3C,MAAM,QAAkB,EAAE;AAE1B,OAAM,KAAK,QAAQ,KAAK,OAAO;AAE/B,KAAI,KAAK,SACP,OAAM,KAAK,QAAQ,KAAK,WAAW;AAGrC,KAAI,KAAK,SAAS,OAChB,OAAM,KAAK,QAAQ,KAAK,OAAO;AAGjC,OAAM,KAAK,UAAU;AACrB,OAAM,KAAK,KAAK,WAAW,GAAG;AAE9B,QAAO,MAAM,KAAK,KAAK;;;;;AAMzB,SAAS,YAAY,MAA0B;AAE7C,QAAO,GADQ,OAAO,KAAK,KAAK,MACf,IAAI,KAAK,WAAW"}
|
|
1
|
+
{"version":3,"file":"read.mjs","names":["parts"],"sources":["../../src/commands/read.ts"],"sourcesContent":["import { type ActionSummary, AFSNotFoundError } from \"@aigne/afs\";\nimport type { AFSRuntime } from \"../runtime.js\";\nimport { isActionsPath, isMetaPath } from \"../utils/meta.js\";\nimport type { ViewType } from \"./ls.js\";\n\nexport interface ReadResult {\n path: string;\n content?: string | object;\n isMeta?: boolean;\n isAction?: boolean;\n size?: number;\n mimeType?: string;\n childrenCount?: number;\n /** Actions available on this node (from stat) */\n actions?: ActionSummary[];\n meta?: {\n kind?: string;\n kinds?: string[];\n description?: string;\n inputSchema?: Record<string, unknown>;\n required?: string[];\n [key: string]: unknown;\n };\n}\n\n/**\n * Read file content\n */\nexport async function readCommand(runtime: AFSRuntime, path: string): Promise<ReadResult> {\n let result: Awaited<ReturnType<typeof runtime.read>>;\n try {\n result = await runtime.read(path);\n } catch (error) {\n if (error instanceof AFSNotFoundError) {\n return { path, content: undefined };\n }\n throw error;\n }\n\n if (!result.data) {\n return {\n path,\n content: undefined,\n };\n }\n\n const entry = result.data;\n const isMeta = isMetaPath(path);\n const isAction = isActionsPath(path);\n\n // For meta paths, content is already an object\n const content = isMeta\n ? entry.content\n : typeof entry.content === \"string\"\n ? entry.content\n : entry.content !== undefined\n ? JSON.stringify(entry.content)\n : undefined;\n\n const readResult: ReadResult = {\n path: entry.path,\n content,\n isMeta,\n isAction,\n size: entry.metadata?.size,\n mimeType: entry.metadata?.mimeType,\n childrenCount: entry.metadata?.childrenCount,\n };\n\n // Try to get actions from stat for non-meta/non-action paths\n if (!isMeta && !isAction) {\n try {\n const statResult = await runtime.stat(path);\n if (statResult.data?.actions && statResult.data.actions.length > 0) {\n readResult.actions = statResult.data.actions;\n }\n } catch {\n // stat not supported, actions will be undefined\n }\n }\n\n // Add meta for all paths (includes kind, kinds, inputSchema for actions)\n const meta = extractMeta(entry.metadata);\n if (meta && Object.keys(meta).length > 0) {\n readResult.meta = meta;\n }\n\n return readResult;\n}\n\n/**\n * Extract meta fields from entry metadata\n * Excludes built-in fields that are exposed separately\n */\nfunction extractMeta(\n metadata: Record<string, unknown> | null | undefined,\n): ReadResult[\"meta\"] | undefined {\n if (!metadata) return undefined;\n\n // Built-in fields that are exposed as separate ReadResult fields\n const builtInFields = new Set([\"type\", \"size\", \"mimeType\", \"childrenCount\"]);\n\n const meta: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(metadata)) {\n if (!builtInFields.has(key) && value !== undefined) {\n meta[key] = value;\n }\n }\n\n return Object.keys(meta).length > 0 ? (meta as ReadResult[\"meta\"]) : undefined;\n}\n\n/**\n * Extract user-defined metadata (excludes system fields)\n */\nfunction extractUserMetadata(meta: ReadResult[\"meta\"]): Record<string, unknown> {\n if (!meta) return {};\n const systemFields = new Set([\n \"kind\",\n \"kinds\",\n \"childrenCount\",\n \"inputSchema\",\n \"required\",\n \"description\",\n ]);\n return Object.fromEntries(Object.entries(meta).filter(([k]) => !systemFields.has(k)));\n}\n\n/**\n * Format params summary: format*,path,limit (* = required)\n */\nfunction formatParamsSummary(\n schema: Record<string, unknown> | undefined,\n required: string[] = [],\n): string {\n if (!schema) return \"\";\n const props = (schema as { properties?: Record<string, unknown> }).properties || schema;\n return Object.keys(props)\n .map((name) => (required.includes(name) ? `${name}*` : name))\n .join(\",\");\n}\n\n/**\n * Check if entry is executable (has afs:executable in kinds)\n */\nfunction isExecutable(meta: ReadResult[\"meta\"]): boolean {\n if (!meta?.kinds) return false;\n const kinds = meta.kinds as string[];\n return kinds.includes(\"afs:executable\");\n}\n\n/**\n * Format read output for different views\n */\nexport function formatReadOutput(read: ReadResult, view: ViewType): string {\n switch (view) {\n case \"json\":\n return formatJson(read);\n case \"llm\":\n return formatLlm(read);\n case \"human\":\n return formatHuman(read);\n default:\n return formatDefault(read);\n }\n}\n\n/**\n * Default format: Raw content or one-liner summary\n * - If content exists: show raw content\n * - If no content (directories): show one-liner summary /path kind children=N actions=a,b,c\n * - For executables: /path afs:executable params=format*,path,limit\n * - For meta paths: raw JSON\n */\nfunction formatDefault(read: ReadResult): string {\n // For meta paths, show raw JSON\n if (read.isMeta && typeof read.content === \"object\") {\n return JSON.stringify(read.content, null, 2);\n }\n\n // For executables (actions) without content, show one-liner with params\n if (read.isAction && isExecutable(read.meta)) {\n // If there's content, show it\n if (read.content !== undefined && read.content !== null && read.content !== \"\") {\n return typeof read.content === \"string\"\n ? read.content\n : JSON.stringify(read.content, null, 2);\n }\n // Otherwise show one-liner summary\n const parts = [read.path, \"afs:executable\"];\n if (read.meta?.inputSchema) {\n const params = formatParamsSummary(read.meta.inputSchema, read.meta.required as string[]);\n if (params) {\n parts.push(`params=${params}`);\n }\n }\n return parts.join(\" \");\n }\n\n // If there's content, show raw content\n if (read.content !== undefined && read.content !== null && read.content !== \"\") {\n return typeof read.content === \"string\" ? read.content : JSON.stringify(read.content, null, 2);\n }\n\n // For directories (no content), show one-liner summary\n const parts = [read.path];\n\n if (read.meta?.kind) {\n parts.push(read.meta.kind as string);\n }\n\n if (read.childrenCount !== undefined) {\n parts.push(`children=${read.childrenCount}`);\n }\n\n if (read.actions && read.actions.length > 0) {\n parts.push(`actions=${read.actions.map((a) => a.name).join(\",\")}`);\n }\n\n return parts.join(\" \");\n}\n\n/**\n * JSON format (includes meta field for regular files if present)\n */\nfunction formatJson(read: ReadResult): string {\n return JSON.stringify(read, null, 2);\n}\n\n/**\n * LLM format: Structured text for AI parsing\n */\nfunction formatLlm(read: ReadResult): string {\n const lines: string[] = [];\n\n // Use META for meta paths\n if (read.isMeta) {\n lines.push(`META ${read.path}`);\n\n const content = read.content as Record<string, unknown> | undefined;\n if (content?.kind) {\n lines.push(`KIND ${content.kind}`);\n }\n\n lines.push(`CONTENT`);\n lines.push(JSON.stringify(content));\n return lines.join(\"\\n\");\n }\n\n // For action paths (executables), show EXECUTABLE format\n if (read.isAction && isExecutable(read.meta)) {\n lines.push(`EXECUTABLE ${read.path}`);\n\n if (read.meta?.description) {\n lines.push(`DESCRIPTION \"${read.meta.description}\"`);\n }\n\n // Show parameters\n if (read.meta?.inputSchema) {\n const schema = read.meta.inputSchema as { properties?: Record<string, unknown> };\n const props = schema.properties || {};\n const required = (read.meta.required as string[]) || [];\n\n for (const [name, propDef] of Object.entries(props)) {\n const prop = propDef as Record<string, unknown>;\n const isReq = required.includes(name);\n let paramLine = `PARAM ${name} TYPE ${prop.type || \"any\"}`;\n paramLine += isReq ? \" REQUIRED\" : \" OPTIONAL\";\n\n if (prop.enum) {\n paramLine += ` CHOICES ${(prop.enum as string[]).join(\",\")}`;\n }\n if (prop.default !== undefined) {\n paramLine += ` DEFAULT ${prop.default}`;\n }\n lines.push(paramLine);\n }\n }\n\n return lines.join(\"\\n\");\n }\n\n // For regular nodes\n lines.push(`NODE ${read.path}`);\n\n if (read.meta?.kind) {\n lines.push(`KIND ${read.meta.kind}`);\n }\n if (read.meta?.kinds && Array.isArray(read.meta.kinds) && read.meta.kinds.length > 0) {\n lines.push(`KINDS ${read.meta.kinds.join(\" \")}`);\n }\n\n if (read.childrenCount !== undefined) {\n lines.push(`CHILDREN ${read.childrenCount}`);\n }\n\n if (read.mimeType) {\n lines.push(`MIME ${read.mimeType}`);\n }\n\n if (read.size !== undefined) {\n lines.push(`SIZE ${read.size}`);\n }\n\n // User metadata\n const userMeta = extractUserMetadata(read.meta);\n if (Object.keys(userMeta).length > 0) {\n const metaStr = Object.entries(userMeta)\n .map(([k, v]) => `${k}=${JSON.stringify(v)}`)\n .join(\" \");\n lines.push(`META ${metaStr}`);\n }\n\n // Actions\n if (read.actions && read.actions.length > 0) {\n lines.push(`ACTIONS ${read.actions.map((a) => a.name).join(\" \")}`);\n }\n\n // Content\n if (read.content !== undefined && read.content !== null && read.content !== \"\") {\n lines.push(`CONTENT ${JSON.stringify(read.content)}`);\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Human format: Formatted display with metadata and actions\n */\nfunction formatHuman(read: ReadResult): string {\n const lines: string[] = [];\n\n // For meta paths, show the content as JSON\n if (read.isMeta && typeof read.content === \"object\") {\n lines.push(`Metadata for ${read.path.replace(/\\/.meta$/, \"\")}`);\n lines.push(\"\");\n lines.push(JSON.stringify(read.content, null, 2));\n return lines.join(\"\\n\");\n }\n\n // For action paths (executables), show action definition\n if (read.isAction && isExecutable(read.meta)) {\n const actionName = read.path.split(\"/\").pop() || \"action\";\n lines.push(`Action: ${actionName}`);\n lines.push(`Path: ${read.path}`);\n\n if (read.meta?.description) {\n lines.push(\"\");\n lines.push(\"Description:\");\n lines.push(` ${read.meta.description}`);\n }\n\n // Show parameters from inputSchema\n if (read.meta?.inputSchema) {\n const schema = read.meta.inputSchema as { properties?: Record<string, unknown> };\n const props = schema.properties || {};\n const required = (read.meta.required as string[]) || [];\n\n if (Object.keys(props).length > 0) {\n lines.push(\"\");\n lines.push(\"Parameters:\");\n for (const [name, propDef] of Object.entries(props)) {\n const prop = propDef as Record<string, unknown>;\n const isReq = required.includes(name);\n const typeStr = `<${prop.type || \"any\"}>`;\n const reqLabel = isReq ? \"(required)\" : \"(optional)\";\n let line = ` --${name} ${typeStr}`.padEnd(24);\n line += prop.description || \"\";\n line += ` ${reqLabel}`;\n lines.push(line);\n\n if (prop.enum) {\n lines.push(`${\"\".padEnd(24)}Choices: ${(prop.enum as string[]).join(\", \")}`);\n }\n if (prop.default !== undefined) {\n lines.push(`${\"\".padEnd(24)}Default: ${prop.default}`);\n }\n }\n }\n }\n\n lines.push(\"\");\n lines.push(\"Usage:\");\n lines.push(` afs exec ${read.path}`);\n\n return lines.join(\"\\n\");\n }\n\n // For regular nodes, show enhanced view\n lines.push(read.path);\n lines.push(\"\");\n\n // Kind info\n if (read.meta?.kind) {\n lines.push(`Kind: ${read.meta.kind}`);\n }\n if (read.meta?.kinds && Array.isArray(read.meta.kinds) && read.meta.kinds.length > 0) {\n lines.push(`Kinds: ${read.meta.kinds.join(\" → \")}`);\n }\n\n // Children\n if (read.childrenCount !== undefined) {\n lines.push(`Children: ${read.childrenCount}`);\n }\n\n // User metadata\n const userMeta = extractUserMetadata(read.meta);\n if (Object.keys(userMeta).length > 0) {\n lines.push(\"\");\n lines.push(\"Metadata:\");\n for (const [key, value] of Object.entries(userMeta)) {\n lines.push(` ${key.padEnd(12)} ${formatValue(value)}`);\n }\n }\n\n // Actions\n if (read.actions && read.actions.length > 0) {\n lines.push(\"\");\n lines.push(`Actions: ${read.actions.map((a) => a.name).join(\", \")}`);\n }\n\n // Content\n if (read.content !== undefined && read.content !== null && read.content !== \"\") {\n lines.push(\"\");\n lines.push(\"Content:\");\n lines.push(formatContent(read.content));\n } else if (read.childrenCount !== undefined && read.childrenCount > 0) {\n lines.push(\"\");\n lines.push(\"Content:\");\n lines.push(\" (directory - use 'afs ls' to list children)\");\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction formatValue(value: unknown): string {\n if (value === null || value === undefined) return \"\";\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\" || typeof value === \"boolean\") return String(value);\n if (Array.isArray(value)) return value.join(\", \");\n return JSON.stringify(value);\n}\n\nfunction formatContent(content: unknown): string {\n if (typeof content === \"string\") {\n // Indent content\n return content\n .split(\"\\n\")\n .map((line) => ` ${line}`)\n .join(\"\\n\");\n }\n return ` ${JSON.stringify(content, null, 2).split(\"\\n\").join(\"\\n \")}`;\n}\n"],"mappings":"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"}
|
package/dist/commands/serve.cjs
CHANGED
|
@@ -44,6 +44,7 @@ async function serveCommand(options = {}) {
|
|
|
44
44
|
const readonly = options.readonly ?? serveConfig.readonly ?? false;
|
|
45
45
|
const cors = options.cors ?? serveConfig.cors ?? false;
|
|
46
46
|
const maxBodySize = options.maxBodySize ?? serveConfig.max_body_size ?? 10 * 1024 * 1024;
|
|
47
|
+
const token = options.token ?? serveConfig.token;
|
|
47
48
|
const runtime = await require_runtime.createRuntime();
|
|
48
49
|
const mounts = config.mounts.map((m) => ({
|
|
49
50
|
path: m.path,
|
|
@@ -51,7 +52,8 @@ async function serveCommand(options = {}) {
|
|
|
51
52
|
}));
|
|
52
53
|
const handler = (0, _aigne_afs_http.createAFSHttpHandler)({
|
|
53
54
|
module: createRuntimeModule(runtime, readonly),
|
|
54
|
-
maxBodySize
|
|
55
|
+
maxBodySize,
|
|
56
|
+
token
|
|
55
57
|
});
|
|
56
58
|
const server = (0, node_http.createServer)(async (req, res) => {
|
|
57
59
|
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
package/dist/commands/serve.mjs
CHANGED
|
@@ -43,6 +43,7 @@ async function serveCommand(options = {}) {
|
|
|
43
43
|
const readonly = options.readonly ?? serveConfig.readonly ?? false;
|
|
44
44
|
const cors = options.cors ?? serveConfig.cors ?? false;
|
|
45
45
|
const maxBodySize = options.maxBodySize ?? serveConfig.max_body_size ?? 10 * 1024 * 1024;
|
|
46
|
+
const token = options.token ?? serveConfig.token;
|
|
46
47
|
const runtime = await createRuntime();
|
|
47
48
|
const mounts = config.mounts.map((m) => ({
|
|
48
49
|
path: m.path,
|
|
@@ -50,7 +51,8 @@ async function serveCommand(options = {}) {
|
|
|
50
51
|
}));
|
|
51
52
|
const handler = createAFSHttpHandler({
|
|
52
53
|
module: createRuntimeModule(runtime, readonly),
|
|
53
|
-
maxBodySize
|
|
54
|
+
maxBodySize,
|
|
55
|
+
token
|
|
54
56
|
});
|
|
55
57
|
const server = createServer(async (req, res) => {
|
|
56
58
|
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve.mjs","names":[],"sources":["../../src/commands/serve.ts"],"sourcesContent":["/**\n * AFS Serve Command\n *\n * Starts an HTTP server to expose AFS providers over HTTP transport\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { createServer } from \"node:http\";\nimport type { AFSModule } from \"@aigne/afs\";\nimport { createAFSHttpHandler } from \"@aigne/afs-http\";\nimport { ConfigLoader } from \"../config/loader.js\";\nimport type { MountConfig, ServeConfig } from \"../config/schema.js\";\nimport { type AFSRuntime, createRuntime } from \"../runtime.js\";\nimport { colors } from \"../ui/index.js\";\n\nexport interface ServeOptions {\n host?: string;\n port?: number;\n path?: string;\n readonly?: boolean;\n cors?: boolean;\n maxBodySize?: number;\n}\n\nexport interface ServeResult {\n success: boolean;\n host: string;\n port: number;\n path: string;\n url: string;\n mounts: Array<{ path: string; provider: string }>;\n}\n\n/**\n * Create an AFSModule wrapper around AFSRuntime\n */\nfunction createRuntimeModule(runtime: AFSRuntime, readonly: boolean): AFSModule {\n return {\n name: \"afs-server\",\n accessMode: readonly ? \"readonly\" : \"readwrite\",\n async list(path, options) {\n return runtime.list(path, options);\n },\n async read(path, options) {\n return runtime.read(path, options);\n },\n async write(path, content, options) {\n if (readonly) {\n throw new Error(\"Server is in readonly mode\");\n }\n return runtime.write(path, content, options);\n },\n async delete(path, options) {\n if (readonly) {\n throw new Error(\"Server is in readonly mode\");\n }\n return runtime.delete(path, options);\n },\n async search(path, query, options) {\n return runtime.search(path, query, options);\n },\n };\n}\n\n/**\n * Start HTTP server to expose AFS providers\n */\nexport async function serveCommand(options: ServeOptions = {}): Promise<ServeResult> {\n // Load config to get mount information and serve defaults\n const configLoader = new ConfigLoader();\n const config = await configLoader.load(process.cwd());\n\n // Get serve config from config file (may be partial or undefined)\n const serveConfig: Partial<ServeConfig> = config.serve ?? {};\n\n // Merge: command line > config file > defaults\n const host = options.host ?? serveConfig.host ?? \"localhost\";\n const port = options.port ?? serveConfig.port ?? 3000;\n const basePath = options.path ?? serveConfig.path ?? \"/afs\";\n const readonly = options.readonly ?? serveConfig.readonly ?? false;\n const cors = options.cors ?? serveConfig.cors ?? false;\n const maxBodySize = options.maxBodySize ?? serveConfig.max_body_size ?? 10 * 1024 * 1024;\n\n // Create runtime and load all mounts\n const runtime = await createRuntime();\n const mounts: Array<{ path: string; provider: string }> = config.mounts.map((m: MountConfig) => ({\n path: m.path,\n provider: m.uri,\n }));\n\n // Create AFSModule wrapper\n const module = createRuntimeModule(runtime, readonly);\n\n // Create HTTP handler\n const handler = createAFSHttpHandler({\n module,\n maxBodySize,\n });\n\n // Create HTTP server with path routing\n const server = createServer(async (req: IncomingMessage, res: ServerResponse) => {\n const url = new URL(req.url || \"/\", `http://${req.headers.host}`);\n\n // Log request (default view)\n const timestamp = new Date().toISOString();\n console.error(`${timestamp} ${req.method} ${url.pathname}`);\n\n // CORS support\n if (cors) {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, PUT, DELETE, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type, Authorization\");\n\n if (req.method === \"OPTIONS\") {\n res.writeHead(204);\n res.end();\n return;\n }\n }\n\n // Check if request is for our base path\n if (!url.pathname.startsWith(basePath)) {\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ code: 1, error: \"Not found\" }));\n return;\n }\n\n // Convert Node.js request to Web Standard Request\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (value) {\n headers.append(key, Array.isArray(value) ? value.join(\", \") : value);\n }\n }\n\n // Collect request body\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(chunk);\n }\n const body = Buffer.concat(chunks);\n\n const request = new Request(`http://${req.headers.host}${req.url}`, {\n method: req.method,\n headers,\n body: body.length > 0 ? body : undefined,\n });\n\n try {\n // Call handler\n const response = await handler(request);\n\n // Copy response to Node.js response\n res.writeHead(response.status, {\n \"Content-Type\": response.headers.get(\"Content-Type\") || \"application/json\",\n });\n\n const responseBody = await response.text();\n res.end(responseBody);\n\n // Log response\n console.error(`${timestamp} ${req.method} ${url.pathname} status=${response.status}`);\n } catch (error) {\n console.error(`Error handling request:`, error);\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ code: 5, error: \"Internal server error\" }));\n }\n });\n\n // Start server\n await new Promise<void>((resolve, reject) => {\n server.listen(port, host, () => {\n resolve();\n });\n server.on(\"error\", reject);\n });\n\n // Handle graceful shutdown\n const shutdown = () => {\n console.error(\"\\nShutting down server...\");\n server.close(() => {\n process.exit(0);\n });\n };\n\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n const url = `http://${host}:${port}${basePath}`;\n\n return {\n success: true,\n host,\n port,\n path: basePath,\n url,\n mounts,\n };\n}\n\n/**\n * Format serve result for output\n */\nexport function formatServeOutput(result: ServeResult): string {\n const lines: string[] = [];\n\n lines.push(colors.green(\"AFS HTTP Server starting...\"));\n lines.push(colors.bold(\"Mounted providers:\"));\n\n for (const mount of result.mounts) {\n lines.push(` ${colors.cyan(mount.path.padEnd(20))} ${colors.dim(mount.provider)}`);\n }\n\n lines.push(\"\");\n lines.push(`${colors.dim(\"Listening on:\")} ${colors.brightCyan(result.url)}`);\n lines.push(colors.dim(\"Press Ctrl+C to stop\"));\n\n return lines.join(\"\\n\");\n}\n"],"mappings":";;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"serve.mjs","names":[],"sources":["../../src/commands/serve.ts"],"sourcesContent":["/**\n * AFS Serve Command\n *\n * Starts an HTTP server to expose AFS providers over HTTP transport\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { createServer } from \"node:http\";\nimport type { AFSModule } from \"@aigne/afs\";\nimport { createAFSHttpHandler } from \"@aigne/afs-http\";\nimport { ConfigLoader } from \"../config/loader.js\";\nimport type { MountConfig, ServeConfig } from \"../config/schema.js\";\nimport { type AFSRuntime, createRuntime } from \"../runtime.js\";\nimport { colors } from \"../ui/index.js\";\n\nexport interface ServeOptions {\n host?: string;\n port?: number;\n path?: string;\n readonly?: boolean;\n cors?: boolean;\n maxBodySize?: number;\n token?: string;\n}\n\nexport interface ServeResult {\n success: boolean;\n host: string;\n port: number;\n path: string;\n url: string;\n mounts: Array<{ path: string; provider: string }>;\n}\n\n/**\n * Create an AFSModule wrapper around AFSRuntime\n */\nfunction createRuntimeModule(runtime: AFSRuntime, readonly: boolean): AFSModule {\n return {\n name: \"afs-server\",\n accessMode: readonly ? \"readonly\" : \"readwrite\",\n async list(path, options) {\n return runtime.list(path, options);\n },\n async read(path, options) {\n return runtime.read(path, options);\n },\n async write(path, content, options) {\n if (readonly) {\n throw new Error(\"Server is in readonly mode\");\n }\n return runtime.write(path, content, options);\n },\n async delete(path, options) {\n if (readonly) {\n throw new Error(\"Server is in readonly mode\");\n }\n return runtime.delete(path, options);\n },\n async search(path, query, options) {\n return runtime.search(path, query, options);\n },\n };\n}\n\n/**\n * Start HTTP server to expose AFS providers\n */\nexport async function serveCommand(options: ServeOptions = {}): Promise<ServeResult> {\n // Load config to get mount information and serve defaults\n const configLoader = new ConfigLoader();\n const config = await configLoader.load(process.cwd());\n\n // Get serve config from config file (may be partial or undefined)\n const serveConfig: Partial<ServeConfig> = config.serve ?? {};\n\n // Merge: command line > config file > defaults\n const host = options.host ?? serveConfig.host ?? \"localhost\";\n const port = options.port ?? serveConfig.port ?? 3000;\n const basePath = options.path ?? serveConfig.path ?? \"/afs\";\n const readonly = options.readonly ?? serveConfig.readonly ?? false;\n const cors = options.cors ?? serveConfig.cors ?? false;\n const maxBodySize = options.maxBodySize ?? serveConfig.max_body_size ?? 10 * 1024 * 1024;\n const token = options.token ?? serveConfig.token;\n\n // Create runtime and load all mounts\n const runtime = await createRuntime();\n const mounts: Array<{ path: string; provider: string }> = config.mounts.map((m: MountConfig) => ({\n path: m.path,\n provider: m.uri,\n }));\n\n // Create AFSModule wrapper\n const module = createRuntimeModule(runtime, readonly);\n\n // Create HTTP handler\n const handler = createAFSHttpHandler({\n module,\n maxBodySize,\n token,\n });\n\n // Create HTTP server with path routing\n const server = createServer(async (req: IncomingMessage, res: ServerResponse) => {\n const url = new URL(req.url || \"/\", `http://${req.headers.host}`);\n\n // Log request (default view)\n const timestamp = new Date().toISOString();\n console.error(`${timestamp} ${req.method} ${url.pathname}`);\n\n // CORS support\n if (cors) {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, PUT, DELETE, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type, Authorization\");\n\n if (req.method === \"OPTIONS\") {\n res.writeHead(204);\n res.end();\n return;\n }\n }\n\n // Check if request is for our base path\n if (!url.pathname.startsWith(basePath)) {\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ code: 1, error: \"Not found\" }));\n return;\n }\n\n // Convert Node.js request to Web Standard Request\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (value) {\n headers.append(key, Array.isArray(value) ? value.join(\", \") : value);\n }\n }\n\n // Collect request body\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(chunk);\n }\n const body = Buffer.concat(chunks);\n\n const request = new Request(`http://${req.headers.host}${req.url}`, {\n method: req.method,\n headers,\n body: body.length > 0 ? body : undefined,\n });\n\n try {\n // Call handler\n const response = await handler(request);\n\n // Copy response to Node.js response\n res.writeHead(response.status, {\n \"Content-Type\": response.headers.get(\"Content-Type\") || \"application/json\",\n });\n\n const responseBody = await response.text();\n res.end(responseBody);\n\n // Log response\n console.error(`${timestamp} ${req.method} ${url.pathname} status=${response.status}`);\n } catch (error) {\n console.error(`Error handling request:`, error);\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ code: 5, error: \"Internal server error\" }));\n }\n });\n\n // Start server\n await new Promise<void>((resolve, reject) => {\n server.listen(port, host, () => {\n resolve();\n });\n server.on(\"error\", reject);\n });\n\n // Handle graceful shutdown\n const shutdown = () => {\n console.error(\"\\nShutting down server...\");\n server.close(() => {\n process.exit(0);\n });\n };\n\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n const url = `http://${host}:${port}${basePath}`;\n\n return {\n success: true,\n host,\n port,\n path: basePath,\n url,\n mounts,\n };\n}\n\n/**\n * Format serve result for output\n */\nexport function formatServeOutput(result: ServeResult): string {\n const lines: string[] = [];\n\n lines.push(colors.green(\"AFS HTTP Server starting...\"));\n lines.push(colors.bold(\"Mounted providers:\"));\n\n for (const mount of result.mounts) {\n lines.push(` ${colors.cyan(mount.path.padEnd(20))} ${colors.dim(mount.provider)}`);\n }\n\n lines.push(\"\");\n lines.push(`${colors.dim(\"Listening on:\")} ${colors.brightCyan(result.url)}`);\n lines.push(colors.dim(\"Press Ctrl+C to stop\"));\n\n return lines.join(\"\\n\");\n}\n"],"mappings":"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"}
|
package/dist/commands/stat.cjs
CHANGED
|
@@ -1,42 +1,75 @@
|
|
|
1
|
+
const require_path_utils = require('../path-utils.cjs');
|
|
1
2
|
|
|
2
3
|
//#region src/commands/stat.ts
|
|
4
|
+
/** Core fields that are not metadata */
|
|
5
|
+
const CORE_FIELDS = new Set([
|
|
6
|
+
"path",
|
|
7
|
+
"size",
|
|
8
|
+
"modified",
|
|
9
|
+
"created",
|
|
10
|
+
"hash",
|
|
11
|
+
"provider",
|
|
12
|
+
"childrenCount",
|
|
13
|
+
"actions"
|
|
14
|
+
]);
|
|
15
|
+
/** Built-in metadata fields to exclude (they are exposed as core fields) */
|
|
16
|
+
const EXCLUDED_META_FIELDS = new Set([
|
|
17
|
+
"type",
|
|
18
|
+
"size",
|
|
19
|
+
"mimeType",
|
|
20
|
+
"childrenCount"
|
|
21
|
+
]);
|
|
3
22
|
/**
|
|
4
23
|
* Get stat information for a path
|
|
5
24
|
*/
|
|
6
|
-
async function statCommand(runtime,
|
|
7
|
-
const
|
|
25
|
+
async function statCommand(runtime, cliPath) {
|
|
26
|
+
const { path: canonicalPath } = require_path_utils.parseCliPath(cliPath);
|
|
27
|
+
try {
|
|
28
|
+
const statResult = await runtime.stat(cliPath);
|
|
29
|
+
if (statResult.data) return buildStatResultFromStat({
|
|
30
|
+
...statResult.data,
|
|
31
|
+
path: canonicalPath
|
|
32
|
+
});
|
|
33
|
+
} catch {}
|
|
34
|
+
const readResult = await runtime.read(cliPath);
|
|
8
35
|
if (!readResult.data) {
|
|
9
|
-
const entry
|
|
10
|
-
if (!entry
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
36
|
+
const entry = (await runtime.list(cliPath, { maxDepth: 0 })).data.find((e) => e.path === canonicalPath);
|
|
37
|
+
if (!entry) return { path: canonicalPath };
|
|
38
|
+
return buildStatResult(entry);
|
|
39
|
+
}
|
|
40
|
+
return buildStatResult(readResult.data);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Build StatResult from AFSStatResult.data
|
|
44
|
+
*/
|
|
45
|
+
function buildStatResultFromStat(data) {
|
|
46
|
+
const result = {
|
|
47
|
+
path: data.path,
|
|
48
|
+
size: data.size,
|
|
49
|
+
modified: data.modified?.toISOString(),
|
|
50
|
+
created: data.created?.toISOString(),
|
|
51
|
+
childrenCount: data.childrenCount,
|
|
52
|
+
actions: data.actions
|
|
53
|
+
};
|
|
54
|
+
if (data.meta) {
|
|
55
|
+
for (const [key, value] of Object.entries(data.meta)) if (!EXCLUDED_META_FIELDS.has(key) && value !== void 0) result[key] = value;
|
|
21
56
|
}
|
|
22
|
-
|
|
23
|
-
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Build StatResult from AFSEntry with flattened metadata
|
|
61
|
+
*/
|
|
62
|
+
function buildStatResult(entry) {
|
|
63
|
+
const result = {
|
|
24
64
|
path: entry.path,
|
|
25
|
-
type: mapEntryType(entry.metadata?.type),
|
|
26
65
|
size: entry.metadata?.size,
|
|
27
66
|
modified: entry.updatedAt?.toISOString(),
|
|
28
67
|
created: entry.createdAt?.toISOString()
|
|
29
68
|
};
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
switch (type) {
|
|
33
|
-
case "file": return "file";
|
|
34
|
-
case "directory":
|
|
35
|
-
case "dir": return "directory";
|
|
36
|
-
case "exec": return "exec";
|
|
37
|
-
case "link": return "link";
|
|
38
|
-
default: return "file";
|
|
69
|
+
if (entry.metadata) {
|
|
70
|
+
for (const [key, value] of Object.entries(entry.metadata)) if (!EXCLUDED_META_FIELDS.has(key) && value !== void 0) result[key] = value;
|
|
39
71
|
}
|
|
72
|
+
return result;
|
|
40
73
|
}
|
|
41
74
|
/**
|
|
42
75
|
* Format stat output for different views
|
|
@@ -50,17 +83,31 @@ function formatStatOutput(stat, view) {
|
|
|
50
83
|
}
|
|
51
84
|
}
|
|
52
85
|
/**
|
|
53
|
-
*
|
|
86
|
+
* Get metadata fields from stat result (non-core fields)
|
|
87
|
+
*/
|
|
88
|
+
function getMetaFields(stat) {
|
|
89
|
+
return Object.entries(stat).filter(([key, value]) => !CORE_FIELDS.has(key) && value !== void 0);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Default format: KEY=VALUE pairs (meta fields with META_ prefix)
|
|
54
93
|
*/
|
|
55
94
|
function formatDefault(stat) {
|
|
56
95
|
const lines = [];
|
|
57
96
|
lines.push(`PATH=${stat.path}`);
|
|
58
|
-
lines.push(`
|
|
97
|
+
if (stat.kind) lines.push(`KIND=${stat.kind}`);
|
|
98
|
+
if (stat.kinds && Array.isArray(stat.kinds) && stat.kinds.length > 0) lines.push(`KINDS=${stat.kinds.join(",")}`);
|
|
59
99
|
if (stat.size !== void 0) lines.push(`SIZE=${stat.size}`);
|
|
100
|
+
if (stat.childrenCount !== void 0) lines.push(`CHILDREN=${stat.childrenCount}`);
|
|
60
101
|
if (stat.modified) lines.push(`MODIFIED=${stat.modified}`);
|
|
61
102
|
if (stat.created) lines.push(`CREATED=${stat.created}`);
|
|
62
103
|
if (stat.hash) lines.push(`HASH=${stat.hash}`);
|
|
63
104
|
if (stat.provider) lines.push(`PROVIDER=${stat.provider}`);
|
|
105
|
+
for (const [key, value] of getMetaFields(stat)) {
|
|
106
|
+
if (key === "kind" || key === "kinds") continue;
|
|
107
|
+
const formattedKey = `META_${key.toUpperCase()}`;
|
|
108
|
+
lines.push(`${formattedKey}=${formatMetaValue(value)}`);
|
|
109
|
+
}
|
|
110
|
+
if (stat.actions && stat.actions.length > 0) lines.push(`ACTIONS=${stat.actions.map((a) => a.name).join(",")}`);
|
|
64
111
|
return lines.join("\n");
|
|
65
112
|
}
|
|
66
113
|
/**
|
|
@@ -70,29 +117,57 @@ function formatJson(stat) {
|
|
|
70
117
|
return JSON.stringify(stat, null, 2);
|
|
71
118
|
}
|
|
72
119
|
/**
|
|
73
|
-
* LLM format: Uppercase keys, semantic
|
|
120
|
+
* LLM format: Uppercase keys, semantic (meta fields with META_ prefix)
|
|
74
121
|
*/
|
|
75
122
|
function formatLlm(stat) {
|
|
76
123
|
const lines = [];
|
|
77
|
-
|
|
78
|
-
lines.push(
|
|
79
|
-
lines.push(`
|
|
124
|
+
lines.push(`NODE ${stat.path}`);
|
|
125
|
+
if (stat.kind) lines.push(`KIND ${stat.kind}`);
|
|
126
|
+
if (stat.kinds && Array.isArray(stat.kinds) && stat.kinds.length > 0) lines.push(`KINDS ${stat.kinds.join(" ")}`);
|
|
80
127
|
if (stat.size !== void 0) lines.push(`SIZE ${stat.size}`);
|
|
128
|
+
if (stat.childrenCount !== void 0) lines.push(`CHILDREN ${stat.childrenCount}`);
|
|
81
129
|
if (stat.hash) lines.push(`HASH ${stat.hash}`);
|
|
82
130
|
if (stat.modified) lines.push(`UPDATED ${stat.modified}`);
|
|
131
|
+
for (const [key, value] of getMetaFields(stat)) {
|
|
132
|
+
if (key === "kind" || key === "kinds") continue;
|
|
133
|
+
const formattedKey = `META_${key.toUpperCase()}`;
|
|
134
|
+
lines.push(`${formattedKey} ${formatMetaValue(value)}`);
|
|
135
|
+
}
|
|
136
|
+
if (stat.actions && stat.actions.length > 0) {
|
|
137
|
+
lines.push(`ACTIONS_COUNT ${stat.actions.length}`);
|
|
138
|
+
for (const action of stat.actions) {
|
|
139
|
+
const desc = action.description ? ` "${action.description}"` : "";
|
|
140
|
+
lines.push(`ACTION ${action.name}${desc}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
83
143
|
lines.push(`SIDE_EFFECT NONE`);
|
|
84
144
|
return lines.join("\n");
|
|
85
145
|
}
|
|
86
146
|
/**
|
|
87
|
-
* Human format: Formatted table
|
|
147
|
+
* Human format: Formatted table with all fields inline
|
|
88
148
|
*/
|
|
89
149
|
function formatHuman(stat) {
|
|
90
150
|
const lines = [];
|
|
91
151
|
lines.push(`Path: ${stat.path}`);
|
|
92
|
-
lines.push(`
|
|
152
|
+
if (stat.kind) lines.push(`Kind: ${stat.kind}`);
|
|
153
|
+
if (stat.kinds && Array.isArray(stat.kinds) && stat.kinds.length > 0) lines.push(`Kinds: ${stat.kinds.join(" → ")}`);
|
|
93
154
|
if (stat.size !== void 0) lines.push(`Size: ${formatSize(stat.size)}`);
|
|
155
|
+
if (stat.childrenCount !== void 0) lines.push(`Children: ${stat.childrenCount}`);
|
|
94
156
|
if (stat.modified) lines.push(`Modified: ${formatDate(stat.modified)}`);
|
|
95
157
|
if (stat.provider) lines.push(`Provider: ${stat.provider}`);
|
|
158
|
+
for (const [key, value] of getMetaFields(stat)) {
|
|
159
|
+
if (key === "kind" || key === "kinds") continue;
|
|
160
|
+
const paddedLabel = `${key.charAt(0).toUpperCase() + key.slice(1)}:`.padEnd(10);
|
|
161
|
+
lines.push(`${paddedLabel}${formatMetaValue(value)}`);
|
|
162
|
+
}
|
|
163
|
+
if (stat.actions && stat.actions.length > 0) {
|
|
164
|
+
lines.push("");
|
|
165
|
+
lines.push(`Actions (${stat.actions.length}):`);
|
|
166
|
+
for (const action of stat.actions) {
|
|
167
|
+
const desc = action.description ? ` - ${action.description}` : "";
|
|
168
|
+
lines.push(` • ${action.name}${desc}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
96
171
|
return lines.join("\n");
|
|
97
172
|
}
|
|
98
173
|
function formatSize(bytes) {
|
|
@@ -100,6 +175,13 @@ function formatSize(bytes) {
|
|
|
100
175
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
101
176
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
102
177
|
}
|
|
178
|
+
function formatMetaValue(value) {
|
|
179
|
+
if (value === null || value === void 0) return "";
|
|
180
|
+
if (typeof value === "string") return value;
|
|
181
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
182
|
+
if (Array.isArray(value)) return value.join(", ");
|
|
183
|
+
return JSON.stringify(value);
|
|
184
|
+
}
|
|
103
185
|
function formatDate(isoString) {
|
|
104
186
|
try {
|
|
105
187
|
return new Date(isoString).toLocaleString();
|