@aigne/afs-cli 1.11.0-beta.1 → 1.11.0-beta.3

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 (73) hide show
  1. package/README.md +63 -9
  2. package/dist/_virtual/rolldown_runtime.cjs +29 -0
  3. package/dist/cli.cjs +251 -0
  4. package/dist/cli.d.cts +1 -0
  5. package/dist/cli.mjs +241 -19
  6. package/dist/cli.mjs.map +1 -0
  7. package/dist/commands/exec.cjs +46 -0
  8. package/dist/commands/exec.mjs +45 -0
  9. package/dist/commands/exec.mjs.map +1 -0
  10. package/dist/commands/explain.cjs +244 -0
  11. package/dist/commands/explain.mjs +242 -0
  12. package/dist/commands/explain.mjs.map +1 -0
  13. package/dist/commands/index.cjs +8 -0
  14. package/dist/commands/index.mjs +10 -0
  15. package/dist/commands/ls.cjs +141 -0
  16. package/dist/commands/ls.mjs +140 -0
  17. package/dist/commands/ls.mjs.map +1 -0
  18. package/dist/commands/mount.cjs +170 -0
  19. package/dist/commands/mount.mjs +166 -0
  20. package/dist/commands/mount.mjs.map +1 -0
  21. package/dist/commands/read.cjs +65 -0
  22. package/dist/commands/read.mjs +64 -0
  23. package/dist/commands/read.mjs.map +1 -0
  24. package/dist/commands/serve.cjs +141 -0
  25. package/dist/commands/serve.mjs +140 -0
  26. package/dist/commands/serve.mjs.map +1 -0
  27. package/dist/commands/stat.cjs +113 -0
  28. package/dist/commands/stat.mjs +112 -0
  29. package/dist/commands/stat.mjs.map +1 -0
  30. package/dist/commands/write.cjs +52 -0
  31. package/dist/commands/write.mjs +51 -0
  32. package/dist/commands/write.mjs.map +1 -0
  33. package/dist/config/env.cjs +46 -0
  34. package/dist/config/env.mjs +46 -0
  35. package/dist/config/env.mjs.map +1 -0
  36. package/dist/config/loader.cjs +171 -0
  37. package/dist/config/loader.mjs +169 -0
  38. package/dist/config/loader.mjs.map +1 -0
  39. package/dist/config/provider-factory.cjs +92 -0
  40. package/dist/config/provider-factory.mjs +93 -0
  41. package/dist/config/provider-factory.mjs.map +1 -0
  42. package/dist/config/schema.cjs +36 -0
  43. package/dist/config/schema.mjs +36 -0
  44. package/dist/config/schema.mjs.map +1 -0
  45. package/dist/config/uri-parser.cjs +92 -0
  46. package/dist/config/uri-parser.mjs +92 -0
  47. package/dist/config/uri-parser.mjs.map +1 -0
  48. package/dist/errors.cjs +29 -0
  49. package/dist/errors.mjs +28 -0
  50. package/dist/errors.mjs.map +1 -0
  51. package/dist/index.cjs +3 -0
  52. package/dist/index.d.cts +2 -0
  53. package/dist/index.d.mts +1 -3
  54. package/dist/index.mjs +1 -1
  55. package/dist/runtime.cjs +82 -0
  56. package/dist/runtime.mjs +82 -0
  57. package/dist/runtime.mjs.map +1 -0
  58. package/dist/version.cjs +9 -0
  59. package/dist/version.d.cts +5 -0
  60. package/dist/version.d.cts.map +1 -0
  61. package/dist/version.d.mts +5 -0
  62. package/dist/version.d.mts.map +1 -0
  63. package/dist/version.mjs +9 -0
  64. package/dist/version.mjs.map +1 -0
  65. package/package.json +52 -11
  66. package/.turbo/turbo-build.log +0 -18
  67. package/.turbo/turbo-check-types.log +0 -4
  68. package/dist/version--p6A8sKX.mjs +0 -5
  69. package/src/cli.test.ts +0 -8
  70. package/src/cli.ts +0 -29
  71. package/src/index.ts +0 -7
  72. package/src/version.ts +0 -1
  73. package/tsconfig.json +0 -16
@@ -0,0 +1,65 @@
1
+
2
+ //#region src/commands/read.ts
3
+ /**
4
+ * Read file content
5
+ */
6
+ async function readCommand(runtime, path) {
7
+ const result = await runtime.read(path);
8
+ if (!result.data) return {
9
+ path,
10
+ content: void 0
11
+ };
12
+ const entry = result.data;
13
+ return {
14
+ path: entry.path,
15
+ content: typeof entry.content === "string" ? entry.content : JSON.stringify(entry.content),
16
+ type: entry.metadata?.type === "directory" ? "directory" : "file",
17
+ size: entry.metadata?.size,
18
+ mimeType: entry.metadata?.mimeType
19
+ };
20
+ }
21
+ /**
22
+ * Format read output for different views
23
+ */
24
+ function formatReadOutput(read, view) {
25
+ switch (view) {
26
+ case "json": return formatJson(read);
27
+ case "llm": return formatLlm(read);
28
+ case "human": return formatHuman(read);
29
+ default: return formatDefault(read);
30
+ }
31
+ }
32
+ /**
33
+ * Default format: Raw content
34
+ */
35
+ function formatDefault(read) {
36
+ return read.content ?? "";
37
+ }
38
+ /**
39
+ * JSON format
40
+ */
41
+ function formatJson(read) {
42
+ return JSON.stringify(read, null, 2);
43
+ }
44
+ /**
45
+ * LLM format: Content with header
46
+ */
47
+ function formatLlm(read) {
48
+ const lines = [];
49
+ lines.push(`FILE ${read.path}`);
50
+ if (read.mimeType) lines.push(`MIME ${read.mimeType}`);
51
+ if (read.size !== void 0) lines.push(`SIZE ${read.size}`);
52
+ lines.push(`CONTENT`);
53
+ lines.push(read.content ?? "");
54
+ return lines.join("\n");
55
+ }
56
+ /**
57
+ * Human format: With syntax highlighting placeholder
58
+ */
59
+ function formatHuman(read) {
60
+ return `${`--- ${read.path} ---`}\n${read.content ?? "(empty)"}`;
61
+ }
62
+
63
+ //#endregion
64
+ exports.formatReadOutput = formatReadOutput;
65
+ exports.readCommand = readCommand;
@@ -0,0 +1,64 @@
1
+ //#region src/commands/read.ts
2
+ /**
3
+ * Read file content
4
+ */
5
+ async function readCommand(runtime, path) {
6
+ const result = await runtime.read(path);
7
+ if (!result.data) return {
8
+ path,
9
+ content: void 0
10
+ };
11
+ const entry = result.data;
12
+ return {
13
+ path: entry.path,
14
+ content: typeof entry.content === "string" ? entry.content : JSON.stringify(entry.content),
15
+ type: entry.metadata?.type === "directory" ? "directory" : "file",
16
+ size: entry.metadata?.size,
17
+ mimeType: entry.metadata?.mimeType
18
+ };
19
+ }
20
+ /**
21
+ * Format read output for different views
22
+ */
23
+ function formatReadOutput(read, view) {
24
+ switch (view) {
25
+ case "json": return formatJson(read);
26
+ case "llm": return formatLlm(read);
27
+ case "human": return formatHuman(read);
28
+ default: return formatDefault(read);
29
+ }
30
+ }
31
+ /**
32
+ * Default format: Raw content
33
+ */
34
+ function formatDefault(read) {
35
+ return read.content ?? "";
36
+ }
37
+ /**
38
+ * JSON format
39
+ */
40
+ function formatJson(read) {
41
+ return JSON.stringify(read, null, 2);
42
+ }
43
+ /**
44
+ * LLM format: Content with header
45
+ */
46
+ function formatLlm(read) {
47
+ const lines = [];
48
+ lines.push(`FILE ${read.path}`);
49
+ if (read.mimeType) lines.push(`MIME ${read.mimeType}`);
50
+ if (read.size !== void 0) lines.push(`SIZE ${read.size}`);
51
+ lines.push(`CONTENT`);
52
+ lines.push(read.content ?? "");
53
+ return lines.join("\n");
54
+ }
55
+ /**
56
+ * Human format: With syntax highlighting placeholder
57
+ */
58
+ function formatHuman(read) {
59
+ return `${`--- ${read.path} ---`}\n${read.content ?? "(empty)"}`;
60
+ }
61
+
62
+ //#endregion
63
+ export { formatReadOutput, readCommand };
64
+ //# sourceMappingURL=read.mjs.map
@@ -0,0 +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"}
@@ -0,0 +1,141 @@
1
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
2
+ const require_loader = require('../config/loader.cjs');
3
+ const require_runtime = require('../runtime.cjs');
4
+ let node_http = require("node:http");
5
+ let _aigne_afs_http = require("@aigne/afs-http");
6
+
7
+ //#region src/commands/serve.ts
8
+ /**
9
+ * Create an AFSModule wrapper around AFSRuntime
10
+ */
11
+ function createRuntimeModule(runtime, readonly) {
12
+ return {
13
+ name: "afs-server",
14
+ accessMode: readonly ? "readonly" : "readwrite",
15
+ async list(path, options) {
16
+ return runtime.list(path, options);
17
+ },
18
+ async read(path, options) {
19
+ return runtime.read(path, options);
20
+ },
21
+ async write(path, content, options) {
22
+ if (readonly) throw new Error("Server is in readonly mode");
23
+ return runtime.write(path, content, options);
24
+ },
25
+ async delete(path, options) {
26
+ if (readonly) throw new Error("Server is in readonly mode");
27
+ return runtime.delete(path, options);
28
+ },
29
+ async search(path, query, options) {
30
+ return runtime.search(path, query, options);
31
+ }
32
+ };
33
+ }
34
+ /**
35
+ * Start HTTP server to expose AFS providers
36
+ */
37
+ async function serveCommand(options = {}) {
38
+ const config = await new require_loader.ConfigLoader().load(process.cwd());
39
+ const serveConfig = config.serve ?? {};
40
+ const host = options.host ?? serveConfig.host ?? "localhost";
41
+ const port = options.port ?? serveConfig.port ?? 3e3;
42
+ const basePath = options.path ?? serveConfig.path ?? "/afs";
43
+ const readonly = options.readonly ?? serveConfig.readonly ?? false;
44
+ const cors = options.cors ?? serveConfig.cors ?? false;
45
+ const maxBodySize = options.maxBodySize ?? serveConfig.max_body_size ?? 10 * 1024 * 1024;
46
+ const runtime = await require_runtime.createRuntime();
47
+ const mounts = config.mounts.map((m) => ({
48
+ path: m.path,
49
+ provider: m.uri
50
+ }));
51
+ const handler = (0, _aigne_afs_http.createAFSHttpHandler)({
52
+ module: createRuntimeModule(runtime, readonly),
53
+ maxBodySize
54
+ });
55
+ const server = (0, node_http.createServer)(async (req, res) => {
56
+ const url = new URL(req.url || "/", `http://${req.headers.host}`);
57
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
58
+ console.error(`${timestamp} ${req.method} ${url.pathname}`);
59
+ if (cors) {
60
+ res.setHeader("Access-Control-Allow-Origin", "*");
61
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
62
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
63
+ if (req.method === "OPTIONS") {
64
+ res.writeHead(204);
65
+ res.end();
66
+ return;
67
+ }
68
+ }
69
+ if (!url.pathname.startsWith(basePath)) {
70
+ res.writeHead(404, { "Content-Type": "application/json" });
71
+ res.end(JSON.stringify({
72
+ code: 1,
73
+ error: "Not found"
74
+ }));
75
+ return;
76
+ }
77
+ const headers = new Headers();
78
+ for (const [key, value] of Object.entries(req.headers)) if (value) headers.append(key, Array.isArray(value) ? value.join(", ") : value);
79
+ const chunks = [];
80
+ for await (const chunk of req) chunks.push(chunk);
81
+ const body = Buffer.concat(chunks);
82
+ const request = new Request(`http://${req.headers.host}${req.url}`, {
83
+ method: req.method,
84
+ headers,
85
+ body: body.length > 0 ? body : void 0
86
+ });
87
+ try {
88
+ const response = await handler(request);
89
+ res.writeHead(response.status, { "Content-Type": response.headers.get("Content-Type") || "application/json" });
90
+ const responseBody = await response.text();
91
+ res.end(responseBody);
92
+ console.error(`${timestamp} ${req.method} ${url.pathname} status=${response.status}`);
93
+ } catch (error) {
94
+ console.error(`Error handling request:`, error);
95
+ res.writeHead(500, { "Content-Type": "application/json" });
96
+ res.end(JSON.stringify({
97
+ code: 5,
98
+ error: "Internal server error"
99
+ }));
100
+ }
101
+ });
102
+ await new Promise((resolve, reject) => {
103
+ server.listen(port, host, () => {
104
+ resolve();
105
+ });
106
+ server.on("error", reject);
107
+ });
108
+ const shutdown = () => {
109
+ console.error("\nShutting down server...");
110
+ server.close(() => {
111
+ process.exit(0);
112
+ });
113
+ };
114
+ process.on("SIGINT", shutdown);
115
+ process.on("SIGTERM", shutdown);
116
+ return {
117
+ success: true,
118
+ host,
119
+ port,
120
+ path: basePath,
121
+ url: `http://${host}:${port}${basePath}`,
122
+ mounts
123
+ };
124
+ }
125
+ /**
126
+ * Format serve result for output
127
+ */
128
+ function formatServeOutput(result) {
129
+ const lines = [];
130
+ lines.push("AFS HTTP Server starting...");
131
+ lines.push("Mounted providers:");
132
+ for (const mount of result.mounts) lines.push(` ${mount.path.padEnd(20)} ${mount.provider}`);
133
+ lines.push("");
134
+ lines.push(`Listening on: ${result.url}`);
135
+ lines.push("Press Ctrl+C to stop");
136
+ return lines.join("\n");
137
+ }
138
+
139
+ //#endregion
140
+ exports.formatServeOutput = formatServeOutput;
141
+ exports.serveCommand = serveCommand;
@@ -0,0 +1,140 @@
1
+ import { ConfigLoader } from "../config/loader.mjs";
2
+ import { createRuntime } from "../runtime.mjs";
3
+ import { createServer } from "node:http";
4
+ import { createAFSHttpHandler } from "@aigne/afs-http";
5
+
6
+ //#region src/commands/serve.ts
7
+ /**
8
+ * Create an AFSModule wrapper around AFSRuntime
9
+ */
10
+ function createRuntimeModule(runtime, readonly) {
11
+ return {
12
+ name: "afs-server",
13
+ accessMode: readonly ? "readonly" : "readwrite",
14
+ async list(path, options) {
15
+ return runtime.list(path, options);
16
+ },
17
+ async read(path, options) {
18
+ return runtime.read(path, options);
19
+ },
20
+ async write(path, content, options) {
21
+ if (readonly) throw new Error("Server is in readonly mode");
22
+ return runtime.write(path, content, options);
23
+ },
24
+ async delete(path, options) {
25
+ if (readonly) throw new Error("Server is in readonly mode");
26
+ return runtime.delete(path, options);
27
+ },
28
+ async search(path, query, options) {
29
+ return runtime.search(path, query, options);
30
+ }
31
+ };
32
+ }
33
+ /**
34
+ * Start HTTP server to expose AFS providers
35
+ */
36
+ async function serveCommand(options = {}) {
37
+ const config = await new ConfigLoader().load(process.cwd());
38
+ const serveConfig = config.serve ?? {};
39
+ const host = options.host ?? serveConfig.host ?? "localhost";
40
+ const port = options.port ?? serveConfig.port ?? 3e3;
41
+ const basePath = options.path ?? serveConfig.path ?? "/afs";
42
+ const readonly = options.readonly ?? serveConfig.readonly ?? false;
43
+ const cors = options.cors ?? serveConfig.cors ?? false;
44
+ const maxBodySize = options.maxBodySize ?? serveConfig.max_body_size ?? 10 * 1024 * 1024;
45
+ const runtime = await createRuntime();
46
+ const mounts = config.mounts.map((m) => ({
47
+ path: m.path,
48
+ provider: m.uri
49
+ }));
50
+ const handler = createAFSHttpHandler({
51
+ module: createRuntimeModule(runtime, readonly),
52
+ maxBodySize
53
+ });
54
+ const server = createServer(async (req, res) => {
55
+ const url = new URL(req.url || "/", `http://${req.headers.host}`);
56
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
57
+ console.error(`${timestamp} ${req.method} ${url.pathname}`);
58
+ if (cors) {
59
+ res.setHeader("Access-Control-Allow-Origin", "*");
60
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
61
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
62
+ if (req.method === "OPTIONS") {
63
+ res.writeHead(204);
64
+ res.end();
65
+ return;
66
+ }
67
+ }
68
+ if (!url.pathname.startsWith(basePath)) {
69
+ res.writeHead(404, { "Content-Type": "application/json" });
70
+ res.end(JSON.stringify({
71
+ code: 1,
72
+ error: "Not found"
73
+ }));
74
+ return;
75
+ }
76
+ const headers = new Headers();
77
+ for (const [key, value] of Object.entries(req.headers)) if (value) headers.append(key, Array.isArray(value) ? value.join(", ") : value);
78
+ const chunks = [];
79
+ for await (const chunk of req) chunks.push(chunk);
80
+ const body = Buffer.concat(chunks);
81
+ const request = new Request(`http://${req.headers.host}${req.url}`, {
82
+ method: req.method,
83
+ headers,
84
+ body: body.length > 0 ? body : void 0
85
+ });
86
+ try {
87
+ const response = await handler(request);
88
+ res.writeHead(response.status, { "Content-Type": response.headers.get("Content-Type") || "application/json" });
89
+ const responseBody = await response.text();
90
+ res.end(responseBody);
91
+ console.error(`${timestamp} ${req.method} ${url.pathname} status=${response.status}`);
92
+ } catch (error) {
93
+ console.error(`Error handling request:`, error);
94
+ res.writeHead(500, { "Content-Type": "application/json" });
95
+ res.end(JSON.stringify({
96
+ code: 5,
97
+ error: "Internal server error"
98
+ }));
99
+ }
100
+ });
101
+ await new Promise((resolve, reject) => {
102
+ server.listen(port, host, () => {
103
+ resolve();
104
+ });
105
+ server.on("error", reject);
106
+ });
107
+ const shutdown = () => {
108
+ console.error("\nShutting down server...");
109
+ server.close(() => {
110
+ process.exit(0);
111
+ });
112
+ };
113
+ process.on("SIGINT", shutdown);
114
+ process.on("SIGTERM", shutdown);
115
+ return {
116
+ success: true,
117
+ host,
118
+ port,
119
+ path: basePath,
120
+ url: `http://${host}:${port}${basePath}`,
121
+ mounts
122
+ };
123
+ }
124
+ /**
125
+ * Format serve result for output
126
+ */
127
+ function formatServeOutput(result) {
128
+ const lines = [];
129
+ lines.push("AFS HTTP Server starting...");
130
+ lines.push("Mounted providers:");
131
+ for (const mount of result.mounts) lines.push(` ${mount.path.padEnd(20)} ${mount.provider}`);
132
+ lines.push("");
133
+ lines.push(`Listening on: ${result.url}`);
134
+ lines.push("Press Ctrl+C to stop");
135
+ return lines.join("\n");
136
+ }
137
+
138
+ //#endregion
139
+ export { formatServeOutput, serveCommand };
140
+ //# sourceMappingURL=serve.mjs.map
@@ -0,0 +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\";\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(\"AFS HTTP Server starting...\");\n lines.push(\"Mounted providers:\");\n\n for (const mount of result.mounts) {\n lines.push(` ${mount.path.padEnd(20)} ${mount.provider}`);\n }\n\n lines.push(\"\");\n lines.push(`Listening on: ${result.url}`);\n lines.push(\"Press Ctrl+C to stop\");\n\n return lines.join(\"\\n\");\n}\n"],"mappings":"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"}
@@ -0,0 +1,113 @@
1
+
2
+ //#region src/commands/stat.ts
3
+ /**
4
+ * Get stat information for a path
5
+ */
6
+ async function statCommand(runtime, path) {
7
+ const readResult = await runtime.read(path);
8
+ 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
+ };
21
+ }
22
+ const entry = readResult.data;
23
+ return {
24
+ path: entry.path,
25
+ type: mapEntryType(entry.metadata?.type),
26
+ size: entry.metadata?.size,
27
+ modified: entry.updatedAt?.toISOString(),
28
+ created: entry.createdAt?.toISOString()
29
+ };
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";
39
+ }
40
+ }
41
+ /**
42
+ * Format stat output for different views
43
+ */
44
+ function formatStatOutput(stat, view) {
45
+ switch (view) {
46
+ case "json": return formatJson(stat);
47
+ case "llm": return formatLlm(stat);
48
+ case "human": return formatHuman(stat);
49
+ default: return formatDefault(stat);
50
+ }
51
+ }
52
+ /**
53
+ * Default format: KEY=VALUE pairs
54
+ */
55
+ function formatDefault(stat) {
56
+ const lines = [];
57
+ lines.push(`PATH=${stat.path}`);
58
+ lines.push(`TYPE=${stat.type}`);
59
+ if (stat.size !== void 0) lines.push(`SIZE=${stat.size}`);
60
+ if (stat.modified) lines.push(`MODIFIED=${stat.modified}`);
61
+ if (stat.created) lines.push(`CREATED=${stat.created}`);
62
+ if (stat.hash) lines.push(`HASH=${stat.hash}`);
63
+ if (stat.provider) lines.push(`PROVIDER=${stat.provider}`);
64
+ return lines.join("\n");
65
+ }
66
+ /**
67
+ * JSON format
68
+ */
69
+ function formatJson(stat) {
70
+ return JSON.stringify(stat, null, 2);
71
+ }
72
+ /**
73
+ * LLM format: Uppercase keys, semantic
74
+ */
75
+ function formatLlm(stat) {
76
+ const lines = [];
77
+ const typeLabel = stat.type.toUpperCase();
78
+ lines.push(`${typeLabel} ${stat.path}`);
79
+ lines.push(`TYPE ${typeLabel}`);
80
+ if (stat.size !== void 0) lines.push(`SIZE ${stat.size}`);
81
+ if (stat.hash) lines.push(`HASH ${stat.hash}`);
82
+ if (stat.modified) lines.push(`UPDATED ${stat.modified}`);
83
+ lines.push(`SIDE_EFFECT NONE`);
84
+ return lines.join("\n");
85
+ }
86
+ /**
87
+ * Human format: Formatted table
88
+ */
89
+ function formatHuman(stat) {
90
+ const lines = [];
91
+ lines.push(`Path: ${stat.path}`);
92
+ lines.push(`Type: ${stat.type}`);
93
+ if (stat.size !== void 0) lines.push(`Size: ${formatSize(stat.size)}`);
94
+ if (stat.modified) lines.push(`Modified: ${formatDate(stat.modified)}`);
95
+ if (stat.provider) lines.push(`Provider: ${stat.provider}`);
96
+ return lines.join("\n");
97
+ }
98
+ function formatSize(bytes) {
99
+ if (bytes < 1024) return `${bytes} bytes`;
100
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
101
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
102
+ }
103
+ function formatDate(isoString) {
104
+ try {
105
+ return new Date(isoString).toLocaleString();
106
+ } catch {
107
+ return isoString;
108
+ }
109
+ }
110
+
111
+ //#endregion
112
+ exports.formatStatOutput = formatStatOutput;
113
+ exports.statCommand = statCommand;
@@ -0,0 +1,112 @@
1
+ //#region src/commands/stat.ts
2
+ /**
3
+ * Get stat information for a path
4
+ */
5
+ async function statCommand(runtime, path) {
6
+ const readResult = await runtime.read(path);
7
+ if (!readResult.data) {
8
+ const entry$1 = (await runtime.list(path, { maxDepth: 0 })).data.find((e) => e.path === path);
9
+ if (!entry$1) return {
10
+ path,
11
+ type: "file"
12
+ };
13
+ return {
14
+ path: entry$1.path,
15
+ type: mapEntryType(entry$1.metadata?.type),
16
+ size: entry$1.metadata?.size,
17
+ modified: entry$1.updatedAt?.toISOString(),
18
+ created: entry$1.createdAt?.toISOString()
19
+ };
20
+ }
21
+ const entry = readResult.data;
22
+ return {
23
+ path: entry.path,
24
+ type: mapEntryType(entry.metadata?.type),
25
+ size: entry.metadata?.size,
26
+ modified: entry.updatedAt?.toISOString(),
27
+ created: entry.createdAt?.toISOString()
28
+ };
29
+ }
30
+ function mapEntryType(type) {
31
+ switch (type) {
32
+ case "file": return "file";
33
+ case "directory":
34
+ case "dir": return "directory";
35
+ case "exec": return "exec";
36
+ case "link": return "link";
37
+ default: return "file";
38
+ }
39
+ }
40
+ /**
41
+ * Format stat output for different views
42
+ */
43
+ function formatStatOutput(stat, view) {
44
+ switch (view) {
45
+ case "json": return formatJson(stat);
46
+ case "llm": return formatLlm(stat);
47
+ case "human": return formatHuman(stat);
48
+ default: return formatDefault(stat);
49
+ }
50
+ }
51
+ /**
52
+ * Default format: KEY=VALUE pairs
53
+ */
54
+ function formatDefault(stat) {
55
+ const lines = [];
56
+ lines.push(`PATH=${stat.path}`);
57
+ lines.push(`TYPE=${stat.type}`);
58
+ if (stat.size !== void 0) lines.push(`SIZE=${stat.size}`);
59
+ if (stat.modified) lines.push(`MODIFIED=${stat.modified}`);
60
+ if (stat.created) lines.push(`CREATED=${stat.created}`);
61
+ if (stat.hash) lines.push(`HASH=${stat.hash}`);
62
+ if (stat.provider) lines.push(`PROVIDER=${stat.provider}`);
63
+ return lines.join("\n");
64
+ }
65
+ /**
66
+ * JSON format
67
+ */
68
+ function formatJson(stat) {
69
+ return JSON.stringify(stat, null, 2);
70
+ }
71
+ /**
72
+ * LLM format: Uppercase keys, semantic
73
+ */
74
+ function formatLlm(stat) {
75
+ const lines = [];
76
+ const typeLabel = stat.type.toUpperCase();
77
+ lines.push(`${typeLabel} ${stat.path}`);
78
+ lines.push(`TYPE ${typeLabel}`);
79
+ if (stat.size !== void 0) lines.push(`SIZE ${stat.size}`);
80
+ if (stat.hash) lines.push(`HASH ${stat.hash}`);
81
+ if (stat.modified) lines.push(`UPDATED ${stat.modified}`);
82
+ lines.push(`SIDE_EFFECT NONE`);
83
+ return lines.join("\n");
84
+ }
85
+ /**
86
+ * Human format: Formatted table
87
+ */
88
+ function formatHuman(stat) {
89
+ const lines = [];
90
+ lines.push(`Path: ${stat.path}`);
91
+ lines.push(`Type: ${stat.type}`);
92
+ if (stat.size !== void 0) lines.push(`Size: ${formatSize(stat.size)}`);
93
+ if (stat.modified) lines.push(`Modified: ${formatDate(stat.modified)}`);
94
+ if (stat.provider) lines.push(`Provider: ${stat.provider}`);
95
+ return lines.join("\n");
96
+ }
97
+ function formatSize(bytes) {
98
+ if (bytes < 1024) return `${bytes} bytes`;
99
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
100
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
101
+ }
102
+ function formatDate(isoString) {
103
+ try {
104
+ return new Date(isoString).toLocaleString();
105
+ } catch {
106
+ return isoString;
107
+ }
108
+ }
109
+
110
+ //#endregion
111
+ export { formatStatOutput, statCommand };
112
+ //# sourceMappingURL=stat.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stat.mjs","names":["entry"],"sources":["../../src/commands/stat.ts"],"sourcesContent":["import type { AFSEntry } from \"@aigne/afs\";\nimport type { AFSRuntime } from \"../runtime.js\";\nimport type { ViewType } from \"./ls.js\";\n\nexport interface StatResult {\n path: string;\n type: \"file\" | \"directory\" | \"exec\" | \"link\";\n size?: number;\n modified?: string;\n created?: string;\n hash?: string;\n provider?: string;\n permissions?: string[];\n}\n\n/**\n * Get stat information for a path\n */\nexport async function statCommand(runtime: AFSRuntime, path: string): Promise<StatResult> {\n // Use read to get detailed metadata\n const readResult = await runtime.read(path);\n\n if (!readResult.data) {\n // Try list as fallback\n const listResult = await runtime.list(path, { maxDepth: 0 });\n const entry = listResult.data.find((e: AFSEntry) => e.path === path);\n\n if (!entry) {\n return {\n path,\n type: \"file\",\n };\n }\n\n return {\n path: entry.path,\n type: mapEntryType(entry.metadata?.type),\n size: entry.metadata?.size,\n modified: entry.updatedAt?.toISOString(),\n created: entry.createdAt?.toISOString(),\n };\n }\n\n const entry = readResult.data;\n return {\n path: entry.path,\n type: mapEntryType(entry.metadata?.type),\n size: entry.metadata?.size,\n modified: entry.updatedAt?.toISOString(),\n created: entry.createdAt?.toISOString(),\n };\n}\n\nfunction mapEntryType(type?: string): StatResult[\"type\"] {\n switch (type) {\n case \"file\":\n return \"file\";\n case \"directory\":\n case \"dir\":\n return \"directory\";\n case \"exec\":\n return \"exec\";\n case \"link\":\n return \"link\";\n default:\n return \"file\";\n }\n}\n\n/**\n * Format stat output for different views\n */\nexport function formatStatOutput(stat: StatResult, view: ViewType): string {\n switch (view) {\n case \"json\":\n return formatJson(stat);\n case \"llm\":\n return formatLlm(stat);\n case \"human\":\n return formatHuman(stat);\n default:\n return formatDefault(stat);\n }\n}\n\n/**\n * Default format: KEY=VALUE pairs\n */\nfunction formatDefault(stat: StatResult): string {\n const lines: string[] = [];\n\n lines.push(`PATH=${stat.path}`);\n lines.push(`TYPE=${stat.type}`);\n\n if (stat.size !== undefined) {\n lines.push(`SIZE=${stat.size}`);\n }\n\n if (stat.modified) {\n lines.push(`MODIFIED=${stat.modified}`);\n }\n\n if (stat.created) {\n lines.push(`CREATED=${stat.created}`);\n }\n\n if (stat.hash) {\n lines.push(`HASH=${stat.hash}`);\n }\n\n if (stat.provider) {\n lines.push(`PROVIDER=${stat.provider}`);\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * JSON format\n */\nfunction formatJson(stat: StatResult): string {\n return JSON.stringify(stat, null, 2);\n}\n\n/**\n * LLM format: Uppercase keys, semantic\n */\nfunction formatLlm(stat: StatResult): string {\n const lines: string[] = [];\n\n const typeLabel = stat.type.toUpperCase();\n lines.push(`${typeLabel} ${stat.path}`);\n lines.push(`TYPE ${typeLabel}`);\n\n if (stat.size !== undefined) {\n lines.push(`SIZE ${stat.size}`);\n }\n\n if (stat.hash) {\n lines.push(`HASH ${stat.hash}`);\n }\n\n if (stat.modified) {\n lines.push(`UPDATED ${stat.modified}`);\n }\n\n lines.push(`SIDE_EFFECT NONE`);\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Human format: Formatted table\n */\nfunction formatHuman(stat: StatResult): string {\n const lines: string[] = [];\n\n lines.push(`Path: ${stat.path}`);\n lines.push(`Type: ${stat.type}`);\n\n if (stat.size !== undefined) {\n lines.push(`Size: ${formatSize(stat.size)}`);\n }\n\n if (stat.modified) {\n lines.push(`Modified: ${formatDate(stat.modified)}`);\n }\n\n if (stat.provider) {\n lines.push(`Provider: ${stat.provider}`);\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} bytes`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nfunction formatDate(isoString: string): string {\n try {\n const date = new Date(isoString);\n return date.toLocaleString();\n } catch {\n return isoString;\n }\n}\n"],"mappings":";;;;AAkBA,eAAsB,YAAY,SAAqB,MAAmC;CAExF,MAAM,aAAa,MAAM,QAAQ,KAAK,KAAK;AAE3C,KAAI,CAAC,WAAW,MAAM;EAGpB,MAAMA,WADa,MAAM,QAAQ,KAAK,MAAM,EAAE,UAAU,GAAG,CAAC,EACnC,KAAK,MAAM,MAAgB,EAAE,SAAS,KAAK;AAEpE,MAAI,CAACA,QACH,QAAO;GACL;GACA,MAAM;GACP;AAGH,SAAO;GACL,MAAMA,QAAM;GACZ,MAAM,aAAaA,QAAM,UAAU,KAAK;GACxC,MAAMA,QAAM,UAAU;GACtB,UAAUA,QAAM,WAAW,aAAa;GACxC,SAASA,QAAM,WAAW,aAAa;GACxC;;CAGH,MAAM,QAAQ,WAAW;AACzB,QAAO;EACL,MAAM,MAAM;EACZ,MAAM,aAAa,MAAM,UAAU,KAAK;EACxC,MAAM,MAAM,UAAU;EACtB,UAAU,MAAM,WAAW,aAAa;EACxC,SAAS,MAAM,WAAW,aAAa;EACxC;;AAGH,SAAS,aAAa,MAAmC;AACvD,SAAQ,MAAR;EACE,KAAK,OACH,QAAO;EACT,KAAK;EACL,KAAK,MACH,QAAO;EACT,KAAK,OACH,QAAO;EACT,KAAK,OACH,QAAO;EACT,QACE,QAAO;;;;;;AAOb,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;CAC/C,MAAM,QAAkB,EAAE;AAE1B,OAAM,KAAK,QAAQ,KAAK,OAAO;AAC/B,OAAM,KAAK,QAAQ,KAAK,OAAO;AAE/B,KAAI,KAAK,SAAS,OAChB,OAAM,KAAK,QAAQ,KAAK,OAAO;AAGjC,KAAI,KAAK,SACP,OAAM,KAAK,YAAY,KAAK,WAAW;AAGzC,KAAI,KAAK,QACP,OAAM,KAAK,WAAW,KAAK,UAAU;AAGvC,KAAI,KAAK,KACP,OAAM,KAAK,QAAQ,KAAK,OAAO;AAGjC,KAAI,KAAK,SACP,OAAM,KAAK,YAAY,KAAK,WAAW;AAGzC,QAAO,MAAM,KAAK,KAAK;;;;;AAMzB,SAAS,WAAW,MAA0B;AAC5C,QAAO,KAAK,UAAU,MAAM,MAAM,EAAE;;;;;AAMtC,SAAS,UAAU,MAA0B;CAC3C,MAAM,QAAkB,EAAE;CAE1B,MAAM,YAAY,KAAK,KAAK,aAAa;AACzC,OAAM,KAAK,GAAG,UAAU,GAAG,KAAK,OAAO;AACvC,OAAM,KAAK,QAAQ,YAAY;AAE/B,KAAI,KAAK,SAAS,OAChB,OAAM,KAAK,QAAQ,KAAK,OAAO;AAGjC,KAAI,KAAK,KACP,OAAM,KAAK,QAAQ,KAAK,OAAO;AAGjC,KAAI,KAAK,SACP,OAAM,KAAK,WAAW,KAAK,WAAW;AAGxC,OAAM,KAAK,mBAAmB;AAE9B,QAAO,MAAM,KAAK,KAAK;;;;;AAMzB,SAAS,YAAY,MAA0B;CAC7C,MAAM,QAAkB,EAAE;AAE1B,OAAM,KAAK,aAAa,KAAK,OAAO;AACpC,OAAM,KAAK,aAAa,KAAK,OAAO;AAEpC,KAAI,KAAK,SAAS,OAChB,OAAM,KAAK,aAAa,WAAW,KAAK,KAAK,GAAG;AAGlD,KAAI,KAAK,SACP,OAAM,KAAK,aAAa,WAAW,KAAK,SAAS,GAAG;AAGtD,KAAI,KAAK,SACP,OAAM,KAAK,aAAa,KAAK,WAAW;AAG1C,QAAO,MAAM,KAAK,KAAK;;AAGzB,SAAS,WAAW,OAAuB;AACzC,KAAI,QAAQ,KAAM,QAAO,GAAG,MAAM;AAClC,KAAI,QAAQ,OAAO,KAAM,QAAO,IAAI,QAAQ,MAAM,QAAQ,EAAE,CAAC;AAC7D,QAAO,IAAI,SAAS,OAAO,OAAO,QAAQ,EAAE,CAAC;;AAG/C,SAAS,WAAW,WAA2B;AAC7C,KAAI;AAEF,SADa,IAAI,KAAK,UAAU,CACpB,gBAAgB;SACtB;AACN,SAAO"}