@aigne/afs-cli 1.11.0-beta.10 → 1.11.0-beta.12

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 (154) hide show
  1. package/dist/cli.cjs +3 -2
  2. package/dist/cli.mjs +3 -2
  3. package/dist/cli.mjs.map +1 -1
  4. package/dist/config/afs-loader.cjs +64 -315
  5. package/dist/config/afs-loader.d.cts.map +1 -1
  6. package/dist/config/afs-loader.d.mts +2 -1
  7. package/dist/config/afs-loader.d.mts.map +1 -1
  8. package/dist/config/afs-loader.mjs +59 -310
  9. package/dist/config/afs-loader.mjs.map +1 -1
  10. package/dist/config/credential-helpers.cjs +291 -0
  11. package/dist/config/credential-helpers.d.mts +2 -0
  12. package/dist/config/credential-helpers.mjs +288 -0
  13. package/dist/config/credential-helpers.mjs.map +1 -0
  14. package/dist/config/loader.cjs +3 -1
  15. package/dist/config/loader.mjs +3 -2
  16. package/dist/config/loader.mjs.map +1 -1
  17. package/dist/config/program-install.cjs +276 -0
  18. package/dist/config/program-install.d.mts +1 -0
  19. package/dist/config/program-install.mjs +273 -0
  20. package/dist/config/program-install.mjs.map +1 -0
  21. package/dist/core/commands/connect.cjs +53 -0
  22. package/dist/core/commands/connect.d.mts +2 -0
  23. package/dist/core/commands/connect.mjs +55 -0
  24. package/dist/core/commands/connect.mjs.map +1 -0
  25. package/dist/core/commands/daemon.cjs +207 -0
  26. package/dist/core/commands/daemon.d.mts +2 -0
  27. package/dist/core/commands/daemon.mjs +208 -0
  28. package/dist/core/commands/daemon.mjs.map +1 -0
  29. package/dist/core/commands/explain.cjs +3 -1
  30. package/dist/core/commands/explain.mjs +3 -1
  31. package/dist/core/commands/explain.mjs.map +1 -1
  32. package/dist/core/commands/explore.cjs +47 -12
  33. package/dist/core/commands/explore.mjs +47 -12
  34. package/dist/core/commands/explore.mjs.map +1 -1
  35. package/dist/core/commands/gen-agent-md.cjs +126 -0
  36. package/dist/core/commands/gen-agent-md.d.mts +2 -0
  37. package/dist/core/commands/gen-agent-md.mjs +125 -0
  38. package/dist/core/commands/gen-agent-md.mjs.map +1 -0
  39. package/dist/core/commands/index.cjs +13 -1
  40. package/dist/core/commands/index.d.cts.map +1 -1
  41. package/dist/core/commands/index.d.mts +6 -0
  42. package/dist/core/commands/index.d.mts.map +1 -1
  43. package/dist/core/commands/index.mjs +13 -1
  44. package/dist/core/commands/index.mjs.map +1 -1
  45. package/dist/core/commands/install.cjs +91 -0
  46. package/dist/core/commands/install.d.mts +2 -0
  47. package/dist/core/commands/install.mjs +92 -0
  48. package/dist/core/commands/install.mjs.map +1 -0
  49. package/dist/core/commands/ls.cjs +14 -2
  50. package/dist/core/commands/ls.d.cts +2 -0
  51. package/dist/core/commands/ls.d.cts.map +1 -1
  52. package/dist/core/commands/ls.d.mts +2 -0
  53. package/dist/core/commands/ls.d.mts.map +1 -1
  54. package/dist/core/commands/ls.mjs +14 -2
  55. package/dist/core/commands/ls.mjs.map +1 -1
  56. package/dist/core/commands/mcp-bridge.cjs +201 -0
  57. package/dist/core/commands/mcp-bridge.d.mts +2 -0
  58. package/dist/core/commands/mcp-bridge.mjs +201 -0
  59. package/dist/core/commands/mcp-bridge.mjs.map +1 -0
  60. package/dist/core/commands/read.cjs +20 -7
  61. package/dist/core/commands/read.d.cts +2 -0
  62. package/dist/core/commands/read.d.cts.map +1 -1
  63. package/dist/core/commands/read.d.mts +2 -0
  64. package/dist/core/commands/read.d.mts.map +1 -1
  65. package/dist/core/commands/read.mjs +20 -7
  66. package/dist/core/commands/read.mjs.map +1 -1
  67. package/dist/core/commands/search.cjs +5 -1
  68. package/dist/core/commands/search.mjs +5 -1
  69. package/dist/core/commands/search.mjs.map +1 -1
  70. package/dist/core/commands/stat.mjs.map +1 -1
  71. package/dist/core/commands/types.d.cts +2 -0
  72. package/dist/core/commands/types.d.cts.map +1 -1
  73. package/dist/core/commands/types.d.mts +2 -0
  74. package/dist/core/commands/types.d.mts.map +1 -1
  75. package/dist/core/commands/types.mjs.map +1 -1
  76. package/dist/core/commands/vault.cjs +289 -0
  77. package/dist/core/commands/vault.d.mts +2 -0
  78. package/dist/core/commands/vault.mjs +289 -0
  79. package/dist/core/commands/vault.mjs.map +1 -0
  80. package/dist/core/commands/write.cjs +19 -6
  81. package/dist/core/commands/write.d.cts +2 -1
  82. package/dist/core/commands/write.d.cts.map +1 -1
  83. package/dist/core/commands/write.d.mts +2 -1
  84. package/dist/core/commands/write.d.mts.map +1 -1
  85. package/dist/core/commands/write.mjs +19 -6
  86. package/dist/core/commands/write.mjs.map +1 -1
  87. package/dist/core/executor/index.cjs +95 -19
  88. package/dist/core/executor/index.d.cts +4 -0
  89. package/dist/core/executor/index.d.cts.map +1 -1
  90. package/dist/core/executor/index.d.mts +4 -0
  91. package/dist/core/executor/index.d.mts.map +1 -1
  92. package/dist/core/executor/index.mjs +95 -19
  93. package/dist/core/executor/index.mjs.map +1 -1
  94. package/dist/core/formatters/index.d.mts +1 -0
  95. package/dist/core/formatters/install.cjs +21 -0
  96. package/dist/core/formatters/install.d.mts +1 -0
  97. package/dist/core/formatters/install.mjs +19 -0
  98. package/dist/core/formatters/install.mjs.map +1 -0
  99. package/dist/core/formatters/vault.cjs +36 -0
  100. package/dist/core/formatters/vault.mjs +32 -0
  101. package/dist/core/formatters/vault.mjs.map +1 -0
  102. package/dist/credential/index.d.mts +2 -1
  103. package/dist/credential/mcp-auth-context.cjs +27 -5
  104. package/dist/credential/mcp-auth-context.mjs +27 -5
  105. package/dist/credential/mcp-auth-context.mjs.map +1 -1
  106. package/dist/credential/resolver.cjs +7 -2
  107. package/dist/credential/resolver.mjs +7 -2
  108. package/dist/credential/resolver.mjs.map +1 -1
  109. package/dist/credential/vault-store.d.mts +1 -0
  110. package/dist/daemon/config-manager.cjs +279 -0
  111. package/dist/daemon/config-manager.mjs +279 -0
  112. package/dist/daemon/config-manager.mjs.map +1 -0
  113. package/dist/daemon/manager.cjs +164 -0
  114. package/dist/daemon/manager.mjs +157 -0
  115. package/dist/daemon/manager.mjs.map +1 -0
  116. package/dist/daemon/server.cjs +220 -0
  117. package/dist/daemon/server.mjs +220 -0
  118. package/dist/daemon/server.mjs.map +1 -0
  119. package/dist/mcp/http-transport.cjs +14 -1
  120. package/dist/mcp/http-transport.mjs +14 -1
  121. package/dist/mcp/http-transport.mjs.map +1 -1
  122. package/dist/mcp/server.cjs +4 -2
  123. package/dist/mcp/server.mjs +4 -2
  124. package/dist/mcp/server.mjs.map +1 -1
  125. package/dist/mcp/tools.cjs +62 -12
  126. package/dist/mcp/tools.mjs +62 -12
  127. package/dist/mcp/tools.mjs.map +1 -1
  128. package/dist/program/daemon-integration.cjs +46 -0
  129. package/dist/program/daemon-integration.mjs +45 -0
  130. package/dist/program/daemon-integration.mjs.map +1 -0
  131. package/dist/program/program-manager.cjs +162 -0
  132. package/dist/program/program-manager.mjs +162 -0
  133. package/dist/program/program-manager.mjs.map +1 -0
  134. package/dist/program/trigger-scanner.cjs +148 -0
  135. package/dist/program/trigger-scanner.mjs +148 -0
  136. package/dist/program/trigger-scanner.mjs.map +1 -0
  137. package/dist/providers/vault/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs +11 -0
  138. package/dist/providers/vault/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs +11 -0
  139. package/dist/providers/vault/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs.map +1 -0
  140. package/dist/providers/vault/dist/encrypted-file.cjs +158 -0
  141. package/dist/providers/vault/dist/encrypted-file.mjs +153 -0
  142. package/dist/providers/vault/dist/encrypted-file.mjs.map +1 -0
  143. package/dist/providers/vault/dist/index.cjs +405 -0
  144. package/dist/providers/vault/dist/index.mjs +400 -0
  145. package/dist/providers/vault/dist/index.mjs.map +1 -0
  146. package/dist/providers/vault/dist/key-resolver.cjs +181 -0
  147. package/dist/providers/vault/dist/key-resolver.mjs +180 -0
  148. package/dist/providers/vault/dist/key-resolver.mjs.map +1 -0
  149. package/dist/repl.cjs +105 -14
  150. package/dist/repl.d.cts.map +1 -1
  151. package/dist/repl.d.mts.map +1 -1
  152. package/dist/repl.mjs +105 -14
  153. package/dist/repl.mjs.map +1 -1
  154. package/package.json +29 -22
@@ -0,0 +1,201 @@
1
+ //#region src/core/commands/mcp-bridge.ts
2
+ const noopFormat = () => "";
3
+ /** Backoff config for reconnection */
4
+ const RECONNECT = {
5
+ initialDelay: 500,
6
+ maxDelay: 1e4,
7
+ factor: 1.5
8
+ };
9
+ function createMcpBridgeCommand(options) {
10
+ return {
11
+ command: "mcp",
12
+ describe: "Connect Claude Desktop (or any stdio MCP client) to AFS",
13
+ builder: (yargs) => yargs.option("port", {
14
+ type: "number",
15
+ default: 4900,
16
+ description: "Daemon port"
17
+ }).epilog([
18
+ "Bridges stdio ↔ the running AFS daemon's MCP endpoint.",
19
+ "Auto-starts the daemon if not running.",
20
+ "",
21
+ "Claude Desktop config example:",
22
+ " { \"mcpServers\": { \"AFS\": { \"command\": \"afs\", \"args\": [\"mcp\"] } } }",
23
+ "",
24
+ "This is NOT a standalone MCP server. For that, use:",
25
+ " afs serve --transport mcp-stdio (standalone, loads AFS directly)",
26
+ " afs serve --transport mcp-http (standalone, HTTP transport)"
27
+ ].join("\n")),
28
+ handler: async (argv) => {
29
+ const { getDaemonStatus, spawnDaemon } = await import("../../daemon/manager.mjs");
30
+ const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
31
+ const { StreamableHTTPClientTransport } = await import("@modelcontextprotocol/sdk/client/streamableHttp.js");
32
+ let info = await getDaemonStatus();
33
+ if (!info) {
34
+ console.error("Starting AFS service...");
35
+ try {
36
+ info = await spawnDaemon(argv.port);
37
+ console.error(`AFS service started on port ${info.port}`);
38
+ } catch (err) {
39
+ console.error(`Failed to start service: ${err.message}`);
40
+ options.onResult({
41
+ command: "mcp",
42
+ result: null,
43
+ format: noopFormat
44
+ });
45
+ process.exitCode = 1;
46
+ return;
47
+ }
48
+ }
49
+ const mcpUrl = new URL(info.mcpUrl);
50
+ let http = null;
51
+ let connected = false;
52
+ let reconnecting = false;
53
+ let connectingPromise = null;
54
+ let cachedInitialize = null;
55
+ /**
56
+ * Try to connect to the daemon's MCP endpoint.
57
+ * Does NOT spawn a new daemon — only connects to an existing one.
58
+ * Spawning is done once at startup; reconnect just waits for it to come back.
59
+ *
60
+ * On reconnect (when cachedInitialize is set), transparently replays the
61
+ * initialize handshake so the new daemon session is established before
62
+ * any client requests are forwarded.
63
+ */
64
+ async function connectHttp() {
65
+ try {
66
+ const transport = new StreamableHTTPClientTransport(mcpUrl);
67
+ let swallowNextResponse = false;
68
+ transport.onmessage = async (message) => {
69
+ if (swallowNextResponse) {
70
+ swallowNextResponse = false;
71
+ return;
72
+ }
73
+ try {
74
+ await stdio.send(message);
75
+ } catch (err) {
76
+ console.error(`Bridge receive error: ${err.message}`);
77
+ }
78
+ };
79
+ transport.onclose = () => {
80
+ if (http !== transport) return;
81
+ connected = false;
82
+ http = null;
83
+ console.error("Backend disconnected, will reconnect...");
84
+ scheduleReconnect();
85
+ };
86
+ await transport.start();
87
+ if (cachedInitialize) {
88
+ swallowNextResponse = true;
89
+ await transport.send(cachedInitialize);
90
+ await transport.send({
91
+ jsonrpc: "2.0",
92
+ method: "notifications/initialized"
93
+ });
94
+ console.error("Session re-initialized on new daemon");
95
+ }
96
+ http = transport;
97
+ connected = true;
98
+ return true;
99
+ } catch {
100
+ return false;
101
+ }
102
+ }
103
+ /** Reconnect loop with exponential backoff */
104
+ function scheduleReconnect() {
105
+ if (reconnecting) return;
106
+ reconnecting = true;
107
+ let resolveGate;
108
+ connectingPromise = new Promise((r) => {
109
+ resolveGate = r;
110
+ });
111
+ let delay = RECONNECT.initialDelay;
112
+ const attempt = async () => {
113
+ if (connected) {
114
+ reconnecting = false;
115
+ resolveGate(true);
116
+ connectingPromise = null;
117
+ return;
118
+ }
119
+ if (await connectHttp()) {
120
+ reconnecting = false;
121
+ resolveGate(true);
122
+ connectingPromise = null;
123
+ console.error(`MCP bridge reconnected to ${mcpUrl}`);
124
+ return;
125
+ }
126
+ delay = Math.min(delay * RECONNECT.factor, RECONNECT.maxDelay);
127
+ setTimeout(attempt, delay);
128
+ };
129
+ attempt();
130
+ }
131
+ const stdio = new StdioServerTransport();
132
+ /** Send a JSON-RPC error back to the client */
133
+ async function sendError(id, message) {
134
+ try {
135
+ await stdio.send({
136
+ jsonrpc: "2.0",
137
+ id,
138
+ error: {
139
+ code: -32e3,
140
+ message
141
+ }
142
+ });
143
+ } catch {}
144
+ }
145
+ stdio.onmessage = async (message) => {
146
+ const msg = message;
147
+ if (msg.method === "initialize") cachedInitialize = msg;
148
+ if (!connected && connectingPromise) await connectingPromise;
149
+ if (!connected || !http) {
150
+ if (msg.id != null) await sendError(msg.id, "AFS service unavailable, reconnecting...");
151
+ return;
152
+ }
153
+ try {
154
+ await http.send(message);
155
+ } catch (err) {
156
+ console.error(`Bridge send error: ${err.message}`);
157
+ if (connected) {
158
+ connected = false;
159
+ http = null;
160
+ scheduleReconnect();
161
+ }
162
+ if (connectingPromise) await connectingPromise;
163
+ if (connected && http) try {
164
+ await http.send(message);
165
+ return;
166
+ } catch {}
167
+ if (msg.id != null) await sendError(msg.id, "AFS service unavailable, reconnecting...");
168
+ }
169
+ };
170
+ let shuttingDown = false;
171
+ const shutdown = () => {
172
+ if (shuttingDown) return;
173
+ shuttingDown = true;
174
+ stdio.onclose = void 0;
175
+ if (http) {
176
+ http.onclose = void 0;
177
+ http.close().catch(() => {});
178
+ }
179
+ stdio.close().catch(() => {});
180
+ setTimeout(() => process.exit(0), 50);
181
+ };
182
+ stdio.onclose = () => shutdown();
183
+ process.on("SIGINT", () => shutdown());
184
+ process.on("SIGTERM", () => shutdown());
185
+ connectingPromise = connectHttp();
186
+ const ok = await connectingPromise;
187
+ connectingPromise = null;
188
+ if (ok) console.error(`MCP bridge connected to ${info.mcpUrl}`);
189
+ else {
190
+ console.error(`MCP bridge started, waiting for daemon at ${info.mcpUrl}...`);
191
+ scheduleReconnect();
192
+ }
193
+ await stdio.start();
194
+ await new Promise(() => {});
195
+ }
196
+ };
197
+ }
198
+
199
+ //#endregion
200
+ export { createMcpBridgeCommand };
201
+ //# sourceMappingURL=mcp-bridge.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-bridge.mjs","names":[],"sources":["../../../src/core/commands/mcp-bridge.ts"],"sourcesContent":["/**\n * AFS MCP Bridge Command\n *\n * Stdio-to-HTTP bridge: forwards MCP protocol between stdin/stdout and the running AFS daemon's /mcp endpoint.\n * Unlike `serve --transport mcp-stdio` which runs a full MCP server, this is a pure proxy — no AFS loading needed.\n *\n * Auto-reconnects when the daemon restarts. While disconnected, requests get immediate JSON-RPC errors\n * so Claude Desktop doesn't hang waiting for a timeout.\n */\n\nimport type { JSONRPCMessage } from \"@modelcontextprotocol/sdk/types.js\";\nimport type { CommandModule } from \"yargs\";\nimport type { CommandFactoryOptions } from \"./types.js\";\n\nexport interface McpBridgeArgs {\n port: number;\n}\n\nconst noopFormat = () => \"\";\n\n/** Backoff config for reconnection */\nconst RECONNECT = {\n initialDelay: 500,\n maxDelay: 10_000,\n factor: 1.5,\n} as const;\n\nexport function createMcpBridgeCommand(\n options: CommandFactoryOptions,\n): CommandModule<unknown, McpBridgeArgs> {\n return {\n command: \"mcp\",\n describe: \"Connect Claude Desktop (or any stdio MCP client) to AFS\",\n builder: (yargs) =>\n yargs\n .option(\"port\", {\n type: \"number\",\n default: 4900,\n description: \"Daemon port\",\n })\n .epilog(\n [\n \"Bridges stdio ↔ the running AFS daemon's MCP endpoint.\",\n \"Auto-starts the daemon if not running.\",\n \"\",\n \"Claude Desktop config example:\",\n ' { \"mcpServers\": { \"AFS\": { \"command\": \"afs\", \"args\": [\"mcp\"] } } }',\n \"\",\n \"This is NOT a standalone MCP server. For that, use:\",\n \" afs serve --transport mcp-stdio (standalone, loads AFS directly)\",\n \" afs serve --transport mcp-http (standalone, HTTP transport)\",\n ].join(\"\\n\"),\n ),\n handler: async (argv) => {\n const { getDaemonStatus, spawnDaemon } = await import(\"../../daemon/manager.js\");\n const { StdioServerTransport } = await import(\"@modelcontextprotocol/sdk/server/stdio.js\");\n const { StreamableHTTPClientTransport } = await import(\n \"@modelcontextprotocol/sdk/client/streamableHttp.js\"\n );\n\n // Ensure daemon is running\n let info = await getDaemonStatus();\n if (!info) {\n console.error(\"Starting AFS service...\");\n try {\n info = await spawnDaemon(argv.port);\n console.error(`AFS service started on port ${info.port}`);\n } catch (err) {\n console.error(`Failed to start service: ${(err as Error).message}`);\n options.onResult({ command: \"mcp\", result: null, format: noopFormat });\n process.exitCode = 1;\n return;\n }\n }\n\n const mcpUrl = new URL(info.mcpUrl);\n\n // --- Reconnectable HTTP transport state ---\n let http: InstanceType<typeof StreamableHTTPClientTransport> | null = null;\n let connected = false;\n let reconnecting = false;\n // Promise that resolves when an in-flight connectHttp() finishes\n let connectingPromise: Promise<boolean> | null = null;\n // Cache the initialize request so we can replay it on reconnect\n let cachedInitialize: Record<string, unknown> | null = null;\n\n /**\n * Try to connect to the daemon's MCP endpoint.\n * Does NOT spawn a new daemon — only connects to an existing one.\n * Spawning is done once at startup; reconnect just waits for it to come back.\n *\n * On reconnect (when cachedInitialize is set), transparently replays the\n * initialize handshake so the new daemon session is established before\n * any client requests are forwarded.\n */\n async function connectHttp(): Promise<boolean> {\n try {\n const transport = new StreamableHTTPClientTransport(mcpUrl);\n\n // During re-init, suppress forwarding the initialize response to Claude Desktop\n let swallowNextResponse = false;\n\n // Wire http → stdio forwarding\n transport.onmessage = async (message) => {\n if (swallowNextResponse) {\n swallowNextResponse = false;\n return;\n }\n try {\n await stdio.send(message);\n } catch (err) {\n console.error(`Bridge receive error: ${(err as Error).message}`);\n }\n };\n\n transport.onclose = () => {\n // Only handle if this is still the active transport\n if (http !== transport) return;\n connected = false;\n http = null;\n console.error(\"Backend disconnected, will reconnect...\");\n scheduleReconnect();\n };\n\n await transport.start();\n\n // On reconnect, replay the initialize handshake transparently\n if (cachedInitialize) {\n swallowNextResponse = true;\n await transport.send(cachedInitialize as JSONRPCMessage);\n // Send initialized notification to complete the handshake\n await transport.send({ jsonrpc: \"2.0\", method: \"notifications/initialized\" });\n console.error(\"Session re-initialized on new daemon\");\n }\n\n http = transport;\n connected = true;\n return true;\n } catch {\n return false;\n }\n }\n\n /** Reconnect loop with exponential backoff */\n function scheduleReconnect() {\n if (reconnecting) return;\n reconnecting = true;\n\n // Create a gate promise immediately — incoming requests can await this\n // instead of getting an instant error during the reconnect window.\n let resolveGate!: (ok: boolean) => void;\n connectingPromise = new Promise<boolean>((r) => {\n resolveGate = r;\n });\n\n let delay: number = RECONNECT.initialDelay;\n const attempt = async () => {\n if (connected) {\n reconnecting = false;\n resolveGate(true);\n connectingPromise = null;\n return;\n }\n\n const ok = await connectHttp();\n\n if (ok) {\n reconnecting = false;\n resolveGate(true);\n connectingPromise = null;\n console.error(`MCP bridge reconnected to ${mcpUrl}`);\n return;\n }\n\n delay = Math.min(delay * RECONNECT.factor, RECONNECT.maxDelay);\n setTimeout(attempt, delay);\n };\n\n // First attempt immediate — no delay for fastest recovery\n attempt();\n }\n\n // --- Stdio transport (lives for the entire process lifetime) ---\n const stdio = new StdioServerTransport();\n\n /** Send a JSON-RPC error back to the client */\n async function sendError(id: string | number, message: string) {\n try {\n await stdio.send({\n jsonrpc: \"2.0\",\n id,\n error: { code: -32000, message },\n });\n } catch {\n // Client may have disconnected — error response is best-effort\n }\n }\n\n // Bridge: stdio → http (with fast error when disconnected)\n stdio.onmessage = async (message) => {\n const msg = message as Record<string, unknown>;\n\n // Cache the initialize request for replay on reconnect\n if (msg.method === \"initialize\") {\n cachedInitialize = msg;\n }\n\n // If we're in the middle of connecting, wait for it\n if (!connected && connectingPromise) {\n await connectingPromise;\n }\n\n if (!connected || !http) {\n if (msg.id != null) {\n await sendError(msg.id as string | number, \"AFS service unavailable, reconnecting...\");\n }\n return;\n }\n\n try {\n await http.send(message);\n } catch (err) {\n console.error(`Bridge send error: ${(err as Error).message}`);\n // Trigger reconnect\n if (connected) {\n connected = false;\n http = null;\n scheduleReconnect();\n }\n // Wait for reconnection and retry once\n if (connectingPromise) {\n await connectingPromise;\n }\n if (connected && http) {\n try {\n await http.send(message);\n return;\n } catch {\n // Retry also failed\n }\n }\n if (msg.id != null) {\n await sendError(msg.id as string | number, \"AFS service unavailable, reconnecting...\");\n }\n }\n };\n\n let shuttingDown = false;\n const shutdown = () => {\n if (shuttingDown) return;\n shuttingDown = true;\n // Detach onclose handlers BEFORE calling close() to prevent recursion\n stdio.onclose = undefined as never;\n if (http) {\n http.onclose = undefined as never;\n http.close().catch(() => {});\n }\n stdio.close().catch(() => {});\n // Give a tick for cleanup, then exit\n setTimeout(() => process.exit(0), 50);\n };\n\n stdio.onclose = () => shutdown();\n process.on(\"SIGINT\", () => shutdown());\n process.on(\"SIGTERM\", () => shutdown());\n\n // IMPORTANT: Connect HTTP FIRST, then start stdio.\n // This prevents the race where Claude Desktop sends `initialize`\n // before the backend connection is ready.\n connectingPromise = connectHttp();\n const ok = await connectingPromise;\n connectingPromise = null;\n\n if (ok) {\n console.error(`MCP bridge connected to ${info.mcpUrl}`);\n } else {\n console.error(`MCP bridge started, waiting for daemon at ${info.mcpUrl}...`);\n scheduleReconnect();\n }\n\n // Start stdio AFTER HTTP is connected (or reconnect is scheduled)\n await stdio.start();\n\n // Block forever — process lives until stdin closes or signal\n await new Promise(() => {});\n },\n };\n}\n"],"mappings":"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"}
@@ -9,17 +9,30 @@ const require_types = require('./types.cjs');
9
9
  */
10
10
  function createReadCommand(options) {
11
11
  return {
12
- command: "read <path>",
12
+ command: ["read <path>", "cat <path>"],
13
13
  describe: "Read file content",
14
- builder: { path: {
15
- type: "string",
16
- demandOption: true,
17
- description: "Path to read"
18
- } },
14
+ builder: {
15
+ path: {
16
+ type: "string",
17
+ demandOption: true,
18
+ description: "Path to read"
19
+ },
20
+ "start-line": {
21
+ type: "number",
22
+ description: "Start line (1-indexed, inclusive)"
23
+ },
24
+ "end-line": {
25
+ type: "number",
26
+ description: "End line (1-indexed, inclusive). -1 for end of file"
27
+ }
28
+ },
19
29
  handler: async (argv) => {
20
30
  const afs = await require_types.resolveAFS(options);
21
31
  const canonicalPath = require_path_utils.cliPathToCanonical(argv.path);
22
- const result = await afs.read(canonicalPath);
32
+ const result = await afs.read(canonicalPath, {
33
+ startLine: argv.startLine,
34
+ endLine: argv.endLine
35
+ });
23
36
  if (result.data) {
24
37
  result.data.path = argv.path;
25
38
  const isMeta = argv.path.endsWith("/.meta") || argv.path.includes("/.meta/");
@@ -7,6 +7,8 @@ import { CommandModule } from "yargs";
7
7
  */
8
8
  interface ReadArgs {
9
9
  path: string;
10
+ startLine?: number;
11
+ endLine?: number;
10
12
  }
11
13
  /**
12
14
  * Create read command factory
@@ -1 +1 @@
1
- {"version":3,"file":"read.d.cts","names":[],"sources":["../../../src/core/commands/read.ts"],"mappings":";;;;;AAsBA;;UAPiB,QAAA;EACf,IAAA;AAAA;;;;iBAMc,iBAAA,CACd,OAAA,EAAS,qBAAA,GACR,aAAA,UAAuB,QAAA"}
1
+ {"version":3,"file":"read.d.cts","names":[],"sources":["../../../src/core/commands/read.ts"],"mappings":";;;;;;;UAeiB,QAAA;EACf,IAAA;EACA,SAAA;EACA,OAAA;AAAA;;;;iBAMc,iBAAA,CACd,OAAA,EAAS,qBAAA,GACR,aAAA,UAAuB,QAAA"}
@@ -7,6 +7,8 @@ import { CommandModule } from "yargs";
7
7
  */
8
8
  interface ReadArgs {
9
9
  path: string;
10
+ startLine?: number;
11
+ endLine?: number;
10
12
  }
11
13
  /**
12
14
  * Create read command factory
@@ -1 +1 @@
1
- {"version":3,"file":"read.d.mts","names":[],"sources":["../../../src/core/commands/read.ts"],"mappings":";;;;;AAsBA;;UAPiB,QAAA;EACf,IAAA;AAAA;;;;iBAMc,iBAAA,CACd,OAAA,EAAS,qBAAA,GACR,aAAA,UAAuB,QAAA"}
1
+ {"version":3,"file":"read.d.mts","names":[],"sources":["../../../src/core/commands/read.ts"],"mappings":";;;;;;;UAeiB,QAAA;EACf,IAAA;EACA,SAAA;EACA,OAAA;AAAA;;;;iBAMc,iBAAA,CACd,OAAA,EAAS,qBAAA,GACR,aAAA,UAAuB,QAAA"}
@@ -9,17 +9,30 @@ import { resolveAFS } from "./types.mjs";
9
9
  */
10
10
  function createReadCommand(options) {
11
11
  return {
12
- command: "read <path>",
12
+ command: ["read <path>", "cat <path>"],
13
13
  describe: "Read file content",
14
- builder: { path: {
15
- type: "string",
16
- demandOption: true,
17
- description: "Path to read"
18
- } },
14
+ builder: {
15
+ path: {
16
+ type: "string",
17
+ demandOption: true,
18
+ description: "Path to read"
19
+ },
20
+ "start-line": {
21
+ type: "number",
22
+ description: "Start line (1-indexed, inclusive)"
23
+ },
24
+ "end-line": {
25
+ type: "number",
26
+ description: "End line (1-indexed, inclusive). -1 for end of file"
27
+ }
28
+ },
19
29
  handler: async (argv) => {
20
30
  const afs = await resolveAFS(options);
21
31
  const canonicalPath = cliPathToCanonical(argv.path);
22
- const result = await afs.read(canonicalPath);
32
+ const result = await afs.read(canonicalPath, {
33
+ startLine: argv.startLine,
34
+ endLine: argv.endLine
35
+ });
23
36
  if (result.data) {
24
37
  result.data.path = argv.path;
25
38
  const isMeta = argv.path.endsWith("/.meta") || argv.path.includes("/.meta/");
@@ -1 +1 @@
1
- {"version":3,"file":"read.mjs","names":[],"sources":["../../../src/core/commands/read.ts"],"sourcesContent":["/**\n * read Command - Core Implementation\n *\n * Reads file/node content. Accepts AFS instance directly.\n * Returns AFSReadResult directly (no custom type).\n */\n\nimport type { CommandModule } from \"yargs\";\nimport { formatReadOutput } from \"../formatters/index.js\";\nimport { cliPathToCanonical } from \"../path-utils.js\";\nimport { type CommandFactoryOptions, resolveAFS } from \"./types.js\";\n\n/**\n * Read command arguments\n */\nexport interface ReadArgs {\n path: string;\n}\n\n/**\n * Create read command factory\n */\nexport function createReadCommand(\n options: CommandFactoryOptions,\n): CommandModule<unknown, ReadArgs> {\n return {\n command: \"read <path>\",\n describe: \"Read file content\",\n builder: {\n path: {\n type: \"string\",\n demandOption: true,\n description: \"Path to read\",\n },\n },\n handler: async (argv) => {\n const afs = await resolveAFS(options);\n const canonicalPath = cliPathToCanonical(argv.path);\n const result = await afs.read(canonicalPath);\n\n // Use the original CLI path for display (UX-friendly, e.g. /fs instead of $afs/fs)\n if (result.data) {\n result.data.path = argv.path;\n\n // Fetch metadata and actions from stat for non-virtual paths\n const isMeta = argv.path.endsWith(\"/.meta\") || argv.path.includes(\"/.meta/\");\n const isAction = argv.path.includes(\"/.actions\");\n\n if (!isMeta && !isAction) {\n try {\n const statResult = await afs.stat(canonicalPath);\n if (statResult.data) {\n result.data.meta = { ...result.data.meta, ...statResult.data.meta };\n result.data.actions = statResult.data.actions;\n }\n } catch {\n // stat not supported\n }\n }\n }\n\n options.onResult({\n command: \"read\",\n result,\n format: formatReadOutput,\n });\n },\n };\n}\n"],"mappings":";;;;;;;;;AAsBA,SAAgB,kBACd,SACkC;AAClC,QAAO;EACL,SAAS;EACT,UAAU;EACV,SAAS,EACP,MAAM;GACJ,MAAM;GACN,cAAc;GACd,aAAa;GACd,EACF;EACD,SAAS,OAAO,SAAS;GACvB,MAAM,MAAM,MAAM,WAAW,QAAQ;GACrC,MAAM,gBAAgB,mBAAmB,KAAK,KAAK;GACnD,MAAM,SAAS,MAAM,IAAI,KAAK,cAAc;AAG5C,OAAI,OAAO,MAAM;AACf,WAAO,KAAK,OAAO,KAAK;IAGxB,MAAM,SAAS,KAAK,KAAK,SAAS,SAAS,IAAI,KAAK,KAAK,SAAS,UAAU;IAC5E,MAAM,WAAW,KAAK,KAAK,SAAS,YAAY;AAEhD,QAAI,CAAC,UAAU,CAAC,SACd,KAAI;KACF,MAAM,aAAa,MAAM,IAAI,KAAK,cAAc;AAChD,SAAI,WAAW,MAAM;AACnB,aAAO,KAAK,OAAO;OAAE,GAAG,OAAO,KAAK;OAAM,GAAG,WAAW,KAAK;OAAM;AACnE,aAAO,KAAK,UAAU,WAAW,KAAK;;YAElC;;AAMZ,WAAQ,SAAS;IACf,SAAS;IACT;IACA,QAAQ;IACT,CAAC;;EAEL"}
1
+ {"version":3,"file":"read.mjs","names":[],"sources":["../../../src/core/commands/read.ts"],"sourcesContent":["/**\n * read Command - Core Implementation\n *\n * Reads file/node content. Accepts AFS instance directly.\n * Returns AFSReadResult directly (no custom type).\n */\n\nimport type { CommandModule } from \"yargs\";\nimport { formatReadOutput } from \"../formatters/index.js\";\nimport { cliPathToCanonical } from \"../path-utils.js\";\nimport { type CommandFactoryOptions, resolveAFS } from \"./types.js\";\n\n/**\n * Read command arguments\n */\nexport interface ReadArgs {\n path: string;\n startLine?: number;\n endLine?: number;\n}\n\n/**\n * Create read command factory\n */\nexport function createReadCommand(\n options: CommandFactoryOptions,\n): CommandModule<unknown, ReadArgs> {\n return {\n command: [\"read <path>\", \"cat <path>\"],\n describe: \"Read file content\",\n builder: {\n path: {\n type: \"string\",\n demandOption: true,\n description: \"Path to read\",\n },\n \"start-line\": {\n type: \"number\",\n description: \"Start line (1-indexed, inclusive)\",\n },\n \"end-line\": {\n type: \"number\",\n description: \"End line (1-indexed, inclusive). -1 for end of file\",\n },\n },\n handler: async (argv) => {\n const afs = await resolveAFS(options);\n const canonicalPath = cliPathToCanonical(argv.path);\n const result = await afs.read(canonicalPath, {\n startLine: argv.startLine,\n endLine: argv.endLine,\n });\n\n // Use the original CLI path for display (UX-friendly, e.g. /fs instead of $afs/fs)\n if (result.data) {\n result.data.path = argv.path;\n\n // Fetch metadata and actions from stat for non-virtual paths\n const isMeta = argv.path.endsWith(\"/.meta\") || argv.path.includes(\"/.meta/\");\n const isAction = argv.path.includes(\"/.actions\");\n\n if (!isMeta && !isAction) {\n try {\n const statResult = await afs.stat(canonicalPath);\n if (statResult.data) {\n result.data.meta = { ...result.data.meta, ...statResult.data.meta };\n result.data.actions = statResult.data.actions;\n }\n } catch {\n // stat not supported\n }\n }\n }\n\n options.onResult({\n command: \"read\",\n result,\n format: formatReadOutput,\n });\n },\n };\n}\n"],"mappings":";;;;;;;;;AAwBA,SAAgB,kBACd,SACkC;AAClC,QAAO;EACL,SAAS,CAAC,eAAe,aAAa;EACtC,UAAU;EACV,SAAS;GACP,MAAM;IACJ,MAAM;IACN,cAAc;IACd,aAAa;IACd;GACD,cAAc;IACZ,MAAM;IACN,aAAa;IACd;GACD,YAAY;IACV,MAAM;IACN,aAAa;IACd;GACF;EACD,SAAS,OAAO,SAAS;GACvB,MAAM,MAAM,MAAM,WAAW,QAAQ;GACrC,MAAM,gBAAgB,mBAAmB,KAAK,KAAK;GACnD,MAAM,SAAS,MAAM,IAAI,KAAK,eAAe;IAC3C,WAAW,KAAK;IAChB,SAAS,KAAK;IACf,CAAC;AAGF,OAAI,OAAO,MAAM;AACf,WAAO,KAAK,OAAO,KAAK;IAGxB,MAAM,SAAS,KAAK,KAAK,SAAS,SAAS,IAAI,KAAK,KAAK,SAAS,UAAU;IAC5E,MAAM,WAAW,KAAK,KAAK,SAAS,YAAY;AAEhD,QAAI,CAAC,UAAU,CAAC,SACd,KAAI;KACF,MAAM,aAAa,MAAM,IAAI,KAAK,cAAc;AAChD,SAAI,WAAW,MAAM;AACnB,aAAO,KAAK,OAAO;OAAE,GAAG,OAAO,KAAK;OAAM,GAAG,WAAW,KAAK;OAAM;AACnE,aAAO,KAAK,UAAU,WAAW,KAAK;;YAElC;;AAMZ,WAAQ,SAAS;IACf,SAAS;IACT;IACA,QAAQ;IACT,CAAC;;EAEL"}
@@ -9,7 +9,11 @@ const require_types = require('./types.cjs');
9
9
  */
10
10
  function createSearchCommand(options) {
11
11
  return {
12
- command: "search <path> <query>",
12
+ command: [
13
+ "search <path> <query>",
14
+ "grep <path> <query>",
15
+ "find <path> <query>"
16
+ ],
13
17
  describe: "Search for content within an AFS path",
14
18
  builder: {
15
19
  path: {
@@ -9,7 +9,11 @@ import { resolveAFS } from "./types.mjs";
9
9
  */
10
10
  function createSearchCommand(options) {
11
11
  return {
12
- command: "search <path> <query>",
12
+ command: [
13
+ "search <path> <query>",
14
+ "grep <path> <query>",
15
+ "find <path> <query>"
16
+ ],
13
17
  describe: "Search for content within an AFS path",
14
18
  builder: {
15
19
  path: {
@@ -1 +1 @@
1
- {"version":3,"file":"search.mjs","names":[],"sources":["../../../src/core/commands/search.ts"],"sourcesContent":["/**\n * search Command - Core Implementation\n *\n * Searches for content within an AFS path.\n * Returns AFSSearchResult directly (no custom type).\n */\n\nimport type { CommandModule } from \"yargs\";\nimport { formatSearchOutput } from \"../formatters/index.js\";\nimport { cliPathToCanonical } from \"../path-utils.js\";\nimport { type CommandFactoryOptions, resolveAFS } from \"./types.js\";\n\n/**\n * Search command arguments\n */\nexport interface SearchArgs {\n path: string;\n query: string;\n}\n\n/**\n * Create search command factory\n */\nexport function createSearchCommand(\n options: CommandFactoryOptions,\n): CommandModule<unknown, SearchArgs> {\n return {\n command: \"search <path> <query>\",\n describe: \"Search for content within an AFS path\",\n builder: {\n path: {\n type: \"string\",\n demandOption: true,\n description: \"Path to search in\",\n },\n query: {\n type: \"string\",\n demandOption: true,\n description: \"Search query\",\n },\n },\n handler: async (argv) => {\n const afs = await resolveAFS(options);\n const canonicalPath = cliPathToCanonical(argv.path);\n const result = await afs.search(canonicalPath, argv.query);\n options.onResult({\n command: \"search\",\n result,\n format: formatSearchOutput,\n });\n },\n };\n}\n"],"mappings":";;;;;;;;;AAuBA,SAAgB,oBACd,SACoC;AACpC,QAAO;EACL,SAAS;EACT,UAAU;EACV,SAAS;GACP,MAAM;IACJ,MAAM;IACN,cAAc;IACd,aAAa;IACd;GACD,OAAO;IACL,MAAM;IACN,cAAc;IACd,aAAa;IACd;GACF;EACD,SAAS,OAAO,SAAS;GACvB,MAAM,MAAM,MAAM,WAAW,QAAQ;GACrC,MAAM,gBAAgB,mBAAmB,KAAK,KAAK;GACnD,MAAM,SAAS,MAAM,IAAI,OAAO,eAAe,KAAK,MAAM;AAC1D,WAAQ,SAAS;IACf,SAAS;IACT;IACA,QAAQ;IACT,CAAC;;EAEL"}
1
+ {"version":3,"file":"search.mjs","names":[],"sources":["../../../src/core/commands/search.ts"],"sourcesContent":["/**\n * search Command - Core Implementation\n *\n * Searches for content within an AFS path.\n * Returns AFSSearchResult directly (no custom type).\n */\n\nimport type { CommandModule } from \"yargs\";\nimport { formatSearchOutput } from \"../formatters/index.js\";\nimport { cliPathToCanonical } from \"../path-utils.js\";\nimport { type CommandFactoryOptions, resolveAFS } from \"./types.js\";\n\n/**\n * Search command arguments\n */\nexport interface SearchArgs {\n path: string;\n query: string;\n}\n\n/**\n * Create search command factory\n */\nexport function createSearchCommand(\n options: CommandFactoryOptions,\n): CommandModule<unknown, SearchArgs> {\n return {\n command: [\"search <path> <query>\", \"grep <path> <query>\", \"find <path> <query>\"],\n describe: \"Search for content within an AFS path\",\n builder: {\n path: {\n type: \"string\",\n demandOption: true,\n description: \"Path to search in\",\n },\n query: {\n type: \"string\",\n demandOption: true,\n description: \"Search query\",\n },\n },\n handler: async (argv) => {\n const afs = await resolveAFS(options);\n const canonicalPath = cliPathToCanonical(argv.path);\n const result = await afs.search(canonicalPath, argv.query);\n options.onResult({\n command: \"search\",\n result,\n format: formatSearchOutput,\n });\n },\n };\n}\n"],"mappings":";;;;;;;;;AAuBA,SAAgB,oBACd,SACoC;AACpC,QAAO;EACL,SAAS;GAAC;GAAyB;GAAuB;GAAsB;EAChF,UAAU;EACV,SAAS;GACP,MAAM;IACJ,MAAM;IACN,cAAc;IACd,aAAa;IACd;GACD,OAAO;IACL,MAAM;IACN,cAAc;IACd,aAAa;IACd;GACF;EACD,SAAS,OAAO,SAAS;GACvB,MAAM,MAAM,MAAM,WAAW,QAAQ;GACrC,MAAM,gBAAgB,mBAAmB,KAAK,KAAK;GACnD,MAAM,SAAS,MAAM,IAAI,OAAO,eAAe,KAAK,MAAM;AAC1D,WAAQ,SAAS;IACf,SAAS;IACT;IACA,QAAQ;IACT,CAAC;;EAEL"}
@@ -1 +1 @@
1
- {"version":3,"file":"stat.mjs","names":[],"sources":["../../../src/core/commands/stat.ts"],"sourcesContent":["/**\n * stat Command - Core Implementation\n *\n * Gets file/node statistics. Accepts AFS instance directly.\n * Returns AFSStatResult directly (no custom type).\n */\n\nimport type { AFSStatResult } from \"@aigne/afs\";\nimport type { CommandModule } from \"yargs\";\nimport { ExitCode } from \"../../errors.js\";\nimport { formatStatOutput } from \"../formatters/index.js\";\nimport { cliPathToCanonical } from \"../path-utils.js\";\nimport { type CommandFactoryOptions, resolveAFS } from \"./types.js\";\n\n/**\n * Stat command arguments\n */\nexport interface StatArgs {\n path: string;\n}\n\n/**\n * Create stat command factory\n */\nexport function createStatCommand(\n options: CommandFactoryOptions,\n): CommandModule<unknown, StatArgs> {\n return {\n command: \"stat <path>\",\n describe: \"Get file or directory info\",\n builder: {\n path: {\n type: \"string\",\n demandOption: true,\n description: \"Path to stat\",\n },\n },\n handler: async (argv) => {\n const afs = await resolveAFS(options);\n const canonicalPath = cliPathToCanonical(argv.path);\n let result: AFSStatResult | undefined;\n try {\n result = await afs.stat(canonicalPath);\n } catch {}\n\n if (!result) {\n // Fallback: Use read to get detailed metadata\n try {\n const entry = (await afs.read(canonicalPath)).data;\n if (entry) {\n result = { data: { ...entry, meta: entry.meta ?? {} } };\n }\n } catch {}\n }\n\n // Use the original CLI path for display (UX-friendly, e.g. /fs instead of $afs/fs)\n if (result?.data) {\n result.data.path = argv.path;\n }\n\n const notFound = !result?.data;\n options.onResult({\n command: \"stat\",\n result: result ?? { data: undefined, message: `No data found for path: ${argv.path}` },\n format: formatStatOutput,\n ...(notFound && {\n error: {\n code: ExitCode.NOT_FOUND,\n message: `No data found for path: ${argv.path}`,\n },\n }),\n });\n },\n };\n}\n"],"mappings":";;;;;;;;;;AAwBA,SAAgB,kBACd,SACkC;AAClC,QAAO;EACL,SAAS;EACT,UAAU;EACV,SAAS,EACP,MAAM;GACJ,MAAM;GACN,cAAc;GACd,aAAa;GACd,EACF;EACD,SAAS,OAAO,SAAS;GACvB,MAAM,MAAM,MAAM,WAAW,QAAQ;GACrC,MAAM,gBAAgB,mBAAmB,KAAK,KAAK;GACnD,IAAI;AACJ,OAAI;AACF,aAAS,MAAM,IAAI,KAAK,cAAc;WAChC;AAER,OAAI,CAAC,OAEH,KAAI;IACF,MAAM,SAAS,MAAM,IAAI,KAAK,cAAc,EAAE;AAC9C,QAAI,MACF,UAAS,EAAE,MAAM;KAAE,GAAG;KAAO,MAAM,MAAM,QAAQ,EAAE;KAAE,EAAE;WAEnD;AAIV,OAAI,QAAQ,KACV,QAAO,KAAK,OAAO,KAAK;GAG1B,MAAM,WAAW,CAAC,QAAQ;AAC1B,WAAQ,SAAS;IACf,SAAS;IACT,QAAQ,UAAU;KAAE,MAAM;KAAW,SAAS,2BAA2B,KAAK;KAAQ;IACtF,QAAQ;IACR,GAAI,YAAY,EACd,OAAO;KACL,MAAM,SAAS;KACf,SAAS,2BAA2B,KAAK;KAC1C,EACF;IACF,CAAC;;EAEL"}
1
+ {"version":3,"file":"stat.mjs","names":[],"sources":["../../../src/core/commands/stat.ts"],"sourcesContent":["/**\n * stat Command - Core Implementation\n *\n * Gets file/node statistics. Accepts AFS instance directly.\n * Returns AFSStatResult directly (no custom type).\n */\n\nimport type { AFSStatResult } from \"@aigne/afs\";\nimport type { CommandModule } from \"yargs\";\nimport { ExitCode } from \"../../errors.js\";\nimport { formatStatOutput } from \"../formatters/index.js\";\nimport { cliPathToCanonical } from \"../path-utils.js\";\nimport { type CommandFactoryOptions, resolveAFS } from \"./types.js\";\n\n/**\n * Stat command arguments\n */\nexport interface StatArgs {\n path: string;\n}\n\n/**\n * Create stat command factory\n */\nexport function createStatCommand(\n options: CommandFactoryOptions,\n): CommandModule<unknown, StatArgs> {\n return {\n command: \"stat <path>\",\n describe: \"Get file or directory info\",\n builder: {\n path: {\n type: \"string\",\n demandOption: true,\n description: \"Path to stat\",\n },\n },\n handler: async (argv) => {\n const afs = await resolveAFS(options);\n const canonicalPath = cliPathToCanonical(argv.path);\n let result: AFSStatResult | undefined;\n try {\n result = await afs.stat(canonicalPath);\n } catch {\n // stat() is optional — provider may not support it; fall through to read()\n }\n\n if (!result) {\n // Fallback: Use read to get detailed metadata\n try {\n const entry = (await afs.read(canonicalPath)).data;\n if (entry) {\n result = { data: { ...entry, meta: entry.meta ?? {} } };\n }\n } catch {\n // read() also failed — result stays undefined, handled below\n }\n }\n\n // Use the original CLI path for display (UX-friendly, e.g. /fs instead of $afs/fs)\n if (result?.data) {\n result.data.path = argv.path;\n }\n\n const notFound = !result?.data;\n options.onResult({\n command: \"stat\",\n result: result ?? { data: undefined, message: `No data found for path: ${argv.path}` },\n format: formatStatOutput,\n ...(notFound && {\n error: {\n code: ExitCode.NOT_FOUND,\n message: `No data found for path: ${argv.path}`,\n },\n }),\n });\n },\n };\n}\n"],"mappings":";;;;;;;;;;AAwBA,SAAgB,kBACd,SACkC;AAClC,QAAO;EACL,SAAS;EACT,UAAU;EACV,SAAS,EACP,MAAM;GACJ,MAAM;GACN,cAAc;GACd,aAAa;GACd,EACF;EACD,SAAS,OAAO,SAAS;GACvB,MAAM,MAAM,MAAM,WAAW,QAAQ;GACrC,MAAM,gBAAgB,mBAAmB,KAAK,KAAK;GACnD,IAAI;AACJ,OAAI;AACF,aAAS,MAAM,IAAI,KAAK,cAAc;WAChC;AAIR,OAAI,CAAC,OAEH,KAAI;IACF,MAAM,SAAS,MAAM,IAAI,KAAK,cAAc,EAAE;AAC9C,QAAI,MACF,UAAS,EAAE,MAAM;KAAE,GAAG;KAAO,MAAM,MAAM,QAAQ,EAAE;KAAE,EAAE;WAEnD;AAMV,OAAI,QAAQ,KACV,QAAO,KAAK,OAAO,KAAK;GAG1B,MAAM,WAAW,CAAC,QAAQ;AAC1B,WAAQ,SAAS;IACf,SAAS;IACT,QAAQ,UAAU;KAAE,MAAM;KAAW,SAAS,2BAA2B,KAAK;KAAQ;IACtF,QAAQ;IACR,GAAI,YAAY,EACd,OAAO;KACL,MAAM,SAAS;KACf,SAAS,2BAA2B,KAAK;KAC1C,EACF;IACF,CAAC;;EAEL"}
@@ -22,6 +22,8 @@ interface CommandOutput {
22
22
  result: unknown;
23
23
  /** Format function to convert result to string */
24
24
  format: FormatFunction;
25
+ /** Override the view type (e.g. -l flag forces "human" view) */
26
+ viewOverride?: ViewType;
25
27
  /** Error info if command failed (presence indicates failure) */
26
28
  error?: {
27
29
  /** Exit code (from ExitCode enum) */code?: number; /** Error message */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.cts","names":[],"sources":["../../../src/core/commands/types.ts"],"mappings":";;;;;;;;;;;KAmBY,cAAA,IAAkB,MAAA,OAAa,IAAA,EAAM,QAAA,EAAU,OAAA;EAAY,IAAA;AAAA;;;;UAKtD,aAAA;EAIf;EAFA,OAAA;EAIQ;EAFR,MAAA;EAME;EAJF,MAAA,EAAQ,cAAA;EAMC;EAJT,KAAA;IAWe,qCATb,IAAA,WAWI;IATJ,OAAA;EAAA;AAAA;;;;UAOa,qBAAA;EAQf;EANA,GAAA,GAAM,GAAA;EAMH;EAJH,IAAA;EAyBwB;EAvBxB,QAAA,GAAW,MAAA,EAAQ,aAAA;EAuByD;EArB5E,GAAA;AAAA;;;;;;;;KAqBU,cAAA,IAAkB,OAAA,EAAS,qBAAA,KAA0B,aAAA"}
1
+ {"version":3,"file":"types.d.cts","names":[],"sources":["../../../src/core/commands/types.ts"],"mappings":";;;;;;;;;;;KAmBY,cAAA,IAAkB,MAAA,OAAa,IAAA,EAAM,QAAA,EAAU,OAAA;EAAY,IAAA;AAAA;;;;UAKtD,aAAA;EAIf;EAFA,OAAA;EAIQ;EAFR,MAAA;EAIe;EAFf,MAAA,EAAQ,cAAA;EAMN;EAJF,YAAA,GAAe,QAAA;EAMN;EAJT,KAAA;IAWe,qCATb,IAAA,WAWI;IATJ,OAAA;EAAA;AAAA;;;;UAOa,qBAAA;EAQf;EANA,GAAA,GAAM,GAAA;EAMH;EAJH,IAAA;EAyBwB;EAvBxB,QAAA,GAAW,MAAA,EAAQ,aAAA;EAuByD;EArB5E,GAAA;AAAA;;;;;;;;KAqBU,cAAA,IAAkB,OAAA,EAAS,qBAAA,KAA0B,aAAA"}
@@ -22,6 +22,8 @@ interface CommandOutput {
22
22
  result: unknown;
23
23
  /** Format function to convert result to string */
24
24
  format: FormatFunction;
25
+ /** Override the view type (e.g. -l flag forces "human" view) */
26
+ viewOverride?: ViewType;
25
27
  /** Error info if command failed (presence indicates failure) */
26
28
  error?: {
27
29
  /** Exit code (from ExitCode enum) */code?: number; /** Error message */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.mts","names":[],"sources":["../../../src/core/commands/types.ts"],"mappings":";;;;;;;;;;;KAmBY,cAAA,IAAkB,MAAA,OAAa,IAAA,EAAM,QAAA,EAAU,OAAA;EAAY,IAAA;AAAA;;;;UAKtD,aAAA;EAIf;EAFA,OAAA;EAIQ;EAFR,MAAA;EAME;EAJF,MAAA,EAAQ,cAAA;EAMC;EAJT,KAAA;IAWe,qCATb,IAAA,WAWI;IATJ,OAAA;EAAA;AAAA;;;;UAOa,qBAAA;EAQf;EANA,GAAA,GAAM,GAAA;EAMH;EAJH,IAAA;EAyBwB;EAvBxB,QAAA,GAAW,MAAA,EAAQ,aAAA;EAuByD;EArB5E,GAAA;AAAA;;;;;;;;KAqBU,cAAA,IAAkB,OAAA,EAAS,qBAAA,KAA0B,aAAA"}
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../../../src/core/commands/types.ts"],"mappings":";;;;;;;;;;;KAmBY,cAAA,IAAkB,MAAA,OAAa,IAAA,EAAM,QAAA,EAAU,OAAA;EAAY,IAAA;AAAA;;;;UAKtD,aAAA;EAIf;EAFA,OAAA;EAIQ;EAFR,MAAA;EAIe;EAFf,MAAA,EAAQ,cAAA;EAMN;EAJF,YAAA,GAAe,QAAA;EAMN;EAJT,KAAA;IAWe,qCATb,IAAA,WAWI;IATJ,OAAA;EAAA;AAAA;;;;UAOa,qBAAA;EAQf;EANA,GAAA,GAAM,GAAA;EAMH;EAJH,IAAA;EAyBwB;EAvBxB,QAAA,GAAW,MAAA,EAAQ,aAAA;EAuByD;EArB5E,GAAA;AAAA;;;;;;;;KAqBU,cAAA,IAAkB,OAAA,EAAS,qBAAA,KAA0B,aAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.mjs","names":[],"sources":["../../../src/core/commands/types.ts"],"sourcesContent":["/**\n * Command Factory Types\n *\n * Defines the interface for yargs CommandModule factories.\n */\n\nimport type { AFS } from \"@aigne/afs\";\nimport type { CommandModule } from \"yargs\";\nimport { loadAFS } from \"../../config/afs-loader.js\";\nimport { createCLIAuthContext } from \"../../credential/cli-auth-context.js\";\nimport { createCredentialStore } from \"../../credential/store.js\";\nimport type { ViewType } from \"../types.js\";\n\n/**\n * Format function type for command output\n *\n * Uses `any` for result type to allow different AFS result types\n * (AFSListResult, AFSReadResult, etc.) to be passed to the formatter.\n */\nexport type FormatFunction = (result: any, view: ViewType, options?: { path?: string }) => string;\n\n/**\n * Command output passed to executor via onResult callback\n */\nexport interface CommandOutput {\n /** The command name (ls, read, write, etc.) */\n command: string;\n /** Raw result from AFS operation */\n result: unknown;\n /** Format function to convert result to string */\n format: FormatFunction;\n /** Error info if command failed (presence indicates failure) */\n error?: {\n /** Exit code (from ExitCode enum) */\n code?: number;\n /** Error message */\n message: string;\n };\n}\n\n/**\n * Options passed to command factory functions\n */\nexport interface CommandFactoryOptions {\n /** AFS instance for executing operations (optional: injected in tests, lazy-loaded in production) */\n afs?: AFS;\n /** Original command line arguments (for pre-parsing in builder) */\n argv: string[];\n /** Callback to pass result back to executor */\n onResult: (output: CommandOutput) => void;\n /** Current working directory (for explore command) */\n cwd?: string;\n}\n\n/**\n * Resolve AFS instance: use injected instance or lazy-load from config\n */\nexport function resolveAFS(options: CommandFactoryOptions): Promise<AFS> {\n if (options.afs) return Promise.resolve(options.afs);\n return loadAFS(options.cwd ?? process.cwd(), {\n authContext: createCLIAuthContext(),\n credentialStore: createCredentialStore(),\n }).then((result) => result.afs);\n}\n\n/**\n * Command factory function type\n *\n * Uses `any` for CommandModule generics to allow different command\n * factories (with different argument types) to be stored in a common array.\n * Each individual factory uses strict types internally.\n */\nexport type CommandFactory = (options: CommandFactoryOptions) => CommandModule<unknown, any>;\n"],"mappings":";;;;;;;;AAyDA,SAAgB,WAAW,SAA8C;AACvE,KAAI,QAAQ,IAAK,QAAO,QAAQ,QAAQ,QAAQ,IAAI;AACpD,QAAO,QAAQ,QAAQ,OAAO,QAAQ,KAAK,EAAE;EAC3C,aAAa,sBAAsB;EACnC,iBAAiB,uBAAuB;EACzC,CAAC,CAAC,MAAM,WAAW,OAAO,IAAI"}
1
+ {"version":3,"file":"types.mjs","names":[],"sources":["../../../src/core/commands/types.ts"],"sourcesContent":["/**\n * Command Factory Types\n *\n * Defines the interface for yargs CommandModule factories.\n */\n\nimport type { AFS } from \"@aigne/afs\";\nimport type { CommandModule } from \"yargs\";\nimport { loadAFS } from \"../../config/afs-loader.js\";\nimport { createCLIAuthContext } from \"../../credential/cli-auth-context.js\";\nimport { createCredentialStore } from \"../../credential/store.js\";\nimport type { ViewType } from \"../types.js\";\n\n/**\n * Format function type for command output\n *\n * Uses `any` for result type to allow different AFS result types\n * (AFSListResult, AFSReadResult, etc.) to be passed to the formatter.\n */\nexport type FormatFunction = (result: any, view: ViewType, options?: { path?: string }) => string;\n\n/**\n * Command output passed to executor via onResult callback\n */\nexport interface CommandOutput {\n /** The command name (ls, read, write, etc.) */\n command: string;\n /** Raw result from AFS operation */\n result: unknown;\n /** Format function to convert result to string */\n format: FormatFunction;\n /** Override the view type (e.g. -l flag forces \"human\" view) */\n viewOverride?: ViewType;\n /** Error info if command failed (presence indicates failure) */\n error?: {\n /** Exit code (from ExitCode enum) */\n code?: number;\n /** Error message */\n message: string;\n };\n}\n\n/**\n * Options passed to command factory functions\n */\nexport interface CommandFactoryOptions {\n /** AFS instance for executing operations (optional: injected in tests, lazy-loaded in production) */\n afs?: AFS;\n /** Original command line arguments (for pre-parsing in builder) */\n argv: string[];\n /** Callback to pass result back to executor */\n onResult: (output: CommandOutput) => void;\n /** Current working directory (for explore command) */\n cwd?: string;\n}\n\n/**\n * Resolve AFS instance: use injected instance or lazy-load from config\n */\nexport function resolveAFS(options: CommandFactoryOptions): Promise<AFS> {\n if (options.afs) return Promise.resolve(options.afs);\n return loadAFS(options.cwd ?? process.cwd(), {\n authContext: createCLIAuthContext(),\n credentialStore: createCredentialStore(),\n }).then((result) => result.afs);\n}\n\n/**\n * Command factory function type\n *\n * Uses `any` for CommandModule generics to allow different command\n * factories (with different argument types) to be stored in a common array.\n * Each individual factory uses strict types internally.\n */\nexport type CommandFactory = (options: CommandFactoryOptions) => CommandModule<unknown, any>;\n"],"mappings":";;;;;;;;AA2DA,SAAgB,WAAW,SAA8C;AACvE,KAAI,QAAQ,IAAK,QAAO,QAAQ,QAAQ,QAAQ,IAAI;AACpD,QAAO,QAAQ,QAAQ,OAAO,QAAQ,KAAK,EAAE;EAC3C,aAAa,sBAAsB;EACnC,iBAAiB,uBAAuB;EACzC,CAAC,CAAC,MAAM,WAAW,OAAO,IAAI"}