@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.
Files changed (72) hide show
  1. package/README.md +16 -1
  2. package/dist/cli.cjs +53 -20
  3. package/dist/cli.mjs +54 -21
  4. package/dist/cli.mjs.map +1 -1
  5. package/dist/commands/exec.cjs +132 -14
  6. package/dist/commands/exec.mjs +129 -14
  7. package/dist/commands/exec.mjs.map +1 -1
  8. package/dist/commands/explain.cjs +1 -1
  9. package/dist/commands/explain.mjs +1 -1
  10. package/dist/commands/explain.mjs.map +1 -1
  11. package/dist/commands/index.mjs +1 -1
  12. package/dist/commands/ls.cjs +129 -30
  13. package/dist/commands/ls.mjs +129 -30
  14. package/dist/commands/ls.mjs.map +1 -1
  15. package/dist/commands/mount.cjs +2 -1
  16. package/dist/commands/mount.mjs +2 -1
  17. package/dist/commands/mount.mjs.map +1 -1
  18. package/dist/commands/read.cjs +213 -14
  19. package/dist/commands/read.mjs +213 -14
  20. package/dist/commands/read.mjs.map +1 -1
  21. package/dist/commands/serve.cjs +3 -1
  22. package/dist/commands/serve.mjs +3 -1
  23. package/dist/commands/serve.mjs.map +1 -1
  24. package/dist/commands/stat.cjs +116 -34
  25. package/dist/commands/stat.mjs +117 -34
  26. package/dist/commands/stat.mjs.map +1 -1
  27. package/dist/commands/write.cjs +37 -4
  28. package/dist/commands/write.mjs +38 -4
  29. package/dist/commands/write.mjs.map +1 -1
  30. package/dist/config/loader.cjs +33 -13
  31. package/dist/config/loader.mjs +33 -13
  32. package/dist/config/loader.mjs.map +1 -1
  33. package/dist/config/provider-factory.cjs +311 -3
  34. package/dist/config/provider-factory.mjs +311 -3
  35. package/dist/config/provider-factory.mjs.map +1 -1
  36. package/dist/config/schema.cjs +3 -1
  37. package/dist/config/schema.mjs +3 -1
  38. package/dist/config/schema.mjs.map +1 -1
  39. package/dist/config/uri-parser.cjs +195 -2
  40. package/dist/config/uri-parser.mjs +195 -2
  41. package/dist/config/uri-parser.mjs.map +1 -1
  42. package/dist/explorer/actions.cjs +53 -23
  43. package/dist/explorer/actions.mjs +54 -23
  44. package/dist/explorer/actions.mjs.map +1 -1
  45. package/dist/explorer/components/dialog.cjs +163 -10
  46. package/dist/explorer/components/dialog.mjs +163 -10
  47. package/dist/explorer/components/dialog.mjs.map +1 -1
  48. package/dist/explorer/components/file-list.mjs.map +1 -1
  49. package/dist/explorer/components/metadata-panel.cjs +39 -25
  50. package/dist/explorer/components/metadata-panel.mjs +39 -25
  51. package/dist/explorer/components/metadata-panel.mjs.map +1 -1
  52. package/dist/explorer/screen.cjs +23 -8
  53. package/dist/explorer/screen.mjs +24 -9
  54. package/dist/explorer/screen.mjs.map +1 -1
  55. package/dist/explorer/theme.cjs +3 -1
  56. package/dist/explorer/theme.mjs +3 -1
  57. package/dist/explorer/theme.mjs.map +1 -1
  58. package/dist/path-utils.cjs +2 -1
  59. package/dist/path-utils.mjs +1 -1
  60. package/dist/runtime.cjs +24 -0
  61. package/dist/runtime.mjs +24 -0
  62. package/dist/runtime.mjs.map +1 -1
  63. package/dist/ui/header.cjs +0 -9
  64. package/dist/ui/header.mjs +1 -9
  65. package/dist/ui/header.mjs.map +1 -1
  66. package/dist/ui/index.cjs +0 -2
  67. package/dist/ui/index.mjs +2 -3
  68. package/dist/ui/index.mjs.map +1 -1
  69. package/dist/utils/meta.cjs +51 -0
  70. package/dist/utils/meta.mjs +49 -0
  71. package/dist/utils/meta.mjs.map +1 -0
  72. package/package.json +19 -9
@@ -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
- const result = await runtime.read(path);
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
- return {
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: typeof entry.content === "string" ? entry.content : JSON.stringify(entry.content),
15
- type: entry.metadata?.type === "directory" ? "directory" : "file",
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
- return read.content ?? "";
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: Content with header
133
+ * LLM format: Structured text for AI parsing
45
134
  */
46
135
  function formatLlm(read) {
47
136
  const lines = [];
48
- lines.push(`FILE ${read.path}`);
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
- lines.push(`CONTENT`);
52
- lines.push(read.content ?? "");
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: With syntax highlighting placeholder
179
+ * Human format: Formatted display with metadata and actions
57
180
  */
58
181
  function formatHuman(read) {
59
- return `${`--- ${read.path} ---`}\n${read.content ?? "(empty)"}`;
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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"}
@@ -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}`);
@@ -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":"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"}
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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"}
@@ -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, path) {
7
- const readResult = await runtime.read(path);
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$1 = (await runtime.list(path, { maxDepth: 0 })).data.find((e) => e.path === path);
10
- if (!entry$1) return {
11
- path,
12
- type: "file"
13
- };
14
- return {
15
- path: entry$1.path,
16
- type: mapEntryType(entry$1.metadata?.type),
17
- size: entry$1.metadata?.size,
18
- modified: entry$1.updatedAt?.toISOString(),
19
- created: entry$1.createdAt?.toISOString()
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
- const entry = readResult.data;
23
- return {
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
- function mapEntryType(type) {
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
- * Default format: KEY=VALUE pairs
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(`TYPE=${stat.type}`);
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
- const typeLabel = stat.type.toUpperCase();
78
- lines.push(`${typeLabel} ${stat.path}`);
79
- lines.push(`TYPE ${typeLabel}`);
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(`Type: ${stat.type}`);
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();