@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,52 @@
1
+
2
+ //#region src/commands/write.ts
3
+ /**
4
+ * Write content to a file
5
+ */
6
+ async function writeCommand(runtime, path, content, options = {}) {
7
+ try {
8
+ const result = await runtime.write(path, { content }, { append: options.append });
9
+ return {
10
+ path: result.data.path,
11
+ success: true,
12
+ size: result.data.metadata?.size
13
+ };
14
+ } catch (error) {
15
+ return {
16
+ path,
17
+ success: false,
18
+ message: error instanceof Error ? error.message : String(error)
19
+ };
20
+ }
21
+ }
22
+ /**
23
+ * Format write output for different views
24
+ */
25
+ function formatWriteOutput(result, view) {
26
+ switch (view) {
27
+ case "json": return JSON.stringify(result, null, 2);
28
+ case "llm": return formatLlm(result);
29
+ case "human": return formatHuman(result);
30
+ default: return formatDefault(result);
31
+ }
32
+ }
33
+ function formatDefault(result) {
34
+ if (result.success) return `OK ${result.path}`;
35
+ return `ERROR ${result.path} ${result.message}`;
36
+ }
37
+ function formatLlm(result) {
38
+ const lines = [];
39
+ lines.push(`WRITE ${result.path}`);
40
+ lines.push(`STATUS ${result.success ? "SUCCESS" : "FAILED"}`);
41
+ if (result.size !== void 0) lines.push(`SIZE ${result.size}`);
42
+ if (result.message) lines.push(`MESSAGE ${result.message}`);
43
+ return lines.join("\n");
44
+ }
45
+ function formatHuman(result) {
46
+ if (result.success) return `Successfully wrote to ${result.path}${result.size ? ` (${result.size} bytes)` : ""}`;
47
+ return `Failed to write to ${result.path}: ${result.message}`;
48
+ }
49
+
50
+ //#endregion
51
+ exports.formatWriteOutput = formatWriteOutput;
52
+ exports.writeCommand = writeCommand;
@@ -0,0 +1,51 @@
1
+ //#region src/commands/write.ts
2
+ /**
3
+ * Write content to a file
4
+ */
5
+ async function writeCommand(runtime, path, content, options = {}) {
6
+ try {
7
+ const result = await runtime.write(path, { content }, { append: options.append });
8
+ return {
9
+ path: result.data.path,
10
+ success: true,
11
+ size: result.data.metadata?.size
12
+ };
13
+ } catch (error) {
14
+ return {
15
+ path,
16
+ success: false,
17
+ message: error instanceof Error ? error.message : String(error)
18
+ };
19
+ }
20
+ }
21
+ /**
22
+ * Format write output for different views
23
+ */
24
+ function formatWriteOutput(result, view) {
25
+ switch (view) {
26
+ case "json": return JSON.stringify(result, null, 2);
27
+ case "llm": return formatLlm(result);
28
+ case "human": return formatHuman(result);
29
+ default: return formatDefault(result);
30
+ }
31
+ }
32
+ function formatDefault(result) {
33
+ if (result.success) return `OK ${result.path}`;
34
+ return `ERROR ${result.path} ${result.message}`;
35
+ }
36
+ function formatLlm(result) {
37
+ const lines = [];
38
+ lines.push(`WRITE ${result.path}`);
39
+ lines.push(`STATUS ${result.success ? "SUCCESS" : "FAILED"}`);
40
+ if (result.size !== void 0) lines.push(`SIZE ${result.size}`);
41
+ if (result.message) lines.push(`MESSAGE ${result.message}`);
42
+ return lines.join("\n");
43
+ }
44
+ function formatHuman(result) {
45
+ if (result.success) return `Successfully wrote to ${result.path}${result.size ? ` (${result.size} bytes)` : ""}`;
46
+ return `Failed to write to ${result.path}: ${result.message}`;
47
+ }
48
+
49
+ //#endregion
50
+ export { formatWriteOutput, writeCommand };
51
+ //# sourceMappingURL=write.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write.mjs","names":[],"sources":["../../src/commands/write.ts"],"sourcesContent":["import type { AFSRuntime } from \"../runtime.js\";\nimport type { ViewType } from \"./ls.js\";\n\nexport interface WriteResult {\n path: string;\n success: boolean;\n size?: number;\n message?: string;\n}\n\n/**\n * Write content to a file\n */\nexport async function writeCommand(\n runtime: AFSRuntime,\n path: string,\n content: string,\n options: { append?: boolean } = {},\n): Promise<WriteResult> {\n try {\n const result = await runtime.write(path, { content }, { append: options.append });\n\n return {\n path: result.data.path,\n success: true,\n size: result.data.metadata?.size,\n };\n } catch (error) {\n return {\n path,\n success: false,\n message: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\n/**\n * Format write output for different views\n */\nexport function formatWriteOutput(result: WriteResult, view: ViewType): string {\n switch (view) {\n case \"json\":\n return JSON.stringify(result, null, 2);\n case \"llm\":\n return formatLlm(result);\n case \"human\":\n return formatHuman(result);\n default:\n return formatDefault(result);\n }\n}\n\nfunction formatDefault(result: WriteResult): string {\n if (result.success) {\n return `OK ${result.path}`;\n }\n return `ERROR ${result.path} ${result.message}`;\n}\n\nfunction formatLlm(result: WriteResult): string {\n const lines: string[] = [];\n\n lines.push(`WRITE ${result.path}`);\n lines.push(`STATUS ${result.success ? \"SUCCESS\" : \"FAILED\"}`);\n\n if (result.size !== undefined) {\n lines.push(`SIZE ${result.size}`);\n }\n\n if (result.message) {\n lines.push(`MESSAGE ${result.message}`);\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction formatHuman(result: WriteResult): string {\n if (result.success) {\n return `Successfully wrote to ${result.path}${result.size ? ` (${result.size} bytes)` : \"\"}`;\n }\n return `Failed to write to ${result.path}: ${result.message}`;\n}\n"],"mappings":";;;;AAaA,eAAsB,aACpB,SACA,MACA,SACA,UAAgC,EAAE,EACZ;AACtB,KAAI;EACF,MAAM,SAAS,MAAM,QAAQ,MAAM,MAAM,EAAE,SAAS,EAAE,EAAE,QAAQ,QAAQ,QAAQ,CAAC;AAEjF,SAAO;GACL,MAAM,OAAO,KAAK;GAClB,SAAS;GACT,MAAM,OAAO,KAAK,UAAU;GAC7B;UACM,OAAO;AACd,SAAO;GACL;GACA,SAAS;GACT,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;GAChE;;;;;;AAOL,SAAgB,kBAAkB,QAAqB,MAAwB;AAC7E,SAAQ,MAAR;EACE,KAAK,OACH,QAAO,KAAK,UAAU,QAAQ,MAAM,EAAE;EACxC,KAAK,MACH,QAAO,UAAU,OAAO;EAC1B,KAAK,QACH,QAAO,YAAY,OAAO;EAC5B,QACE,QAAO,cAAc,OAAO;;;AAIlC,SAAS,cAAc,QAA6B;AAClD,KAAI,OAAO,QACT,QAAO,MAAM,OAAO;AAEtB,QAAO,SAAS,OAAO,KAAK,GAAG,OAAO;;AAGxC,SAAS,UAAU,QAA6B;CAC9C,MAAM,QAAkB,EAAE;AAE1B,OAAM,KAAK,SAAS,OAAO,OAAO;AAClC,OAAM,KAAK,UAAU,OAAO,UAAU,YAAY,WAAW;AAE7D,KAAI,OAAO,SAAS,OAClB,OAAM,KAAK,QAAQ,OAAO,OAAO;AAGnC,KAAI,OAAO,QACT,OAAM,KAAK,WAAW,OAAO,UAAU;AAGzC,QAAO,MAAM,KAAK,KAAK;;AAGzB,SAAS,YAAY,QAA6B;AAChD,KAAI,OAAO,QACT,QAAO,yBAAyB,OAAO,OAAO,OAAO,OAAO,KAAK,OAAO,KAAK,WAAW;AAE1F,QAAO,sBAAsB,OAAO,KAAK,IAAI,OAAO"}
@@ -0,0 +1,46 @@
1
+
2
+ //#region src/config/env.ts
3
+ /**
4
+ * Resolve environment variable references in a string
5
+ *
6
+ * Syntax: ${VAR_NAME}
7
+ * Escape: \${VAR_NAME} (will not be resolved)
8
+ *
9
+ * @param value - String containing ${VAR} references
10
+ * @param options - Resolution options
11
+ * @returns String with env vars resolved
12
+ * @throws Error if env var is undefined and allowUndefined is false
13
+ */
14
+ function resolveEnvVars(value, options = {}) {
15
+ const { allowUndefined = false } = options;
16
+ if (!value) return value;
17
+ const ESCAPE_PLACEHOLDER = "\0ESCAPED_ENV\0";
18
+ let result = value.replace(/\\\$\{/g, ESCAPE_PLACEHOLDER);
19
+ result = result.replace(/\$\{([^}]+)\}/g, (_match, varName) => {
20
+ const envValue = process.env[varName];
21
+ if (envValue === void 0) {
22
+ if (allowUndefined) return "";
23
+ throw new Error(`Environment variable ${varName} is not defined`);
24
+ }
25
+ return envValue;
26
+ });
27
+ result = result.replace(new RegExp(ESCAPE_PLACEHOLDER, "g"), "${");
28
+ return result;
29
+ }
30
+ /**
31
+ * Resolve environment variables in all string fields of an object (deep)
32
+ */
33
+ function resolveEnvVarsInObject(obj, options = {}) {
34
+ if (obj === null || obj === void 0) return obj;
35
+ if (typeof obj === "string") return resolveEnvVars(obj, options);
36
+ if (Array.isArray(obj)) return obj.map((item) => resolveEnvVarsInObject(item, options));
37
+ if (typeof obj === "object") {
38
+ const result = {};
39
+ for (const [key, value] of Object.entries(obj)) result[key] = resolveEnvVarsInObject(value, options);
40
+ return result;
41
+ }
42
+ return obj;
43
+ }
44
+
45
+ //#endregion
46
+ exports.resolveEnvVarsInObject = resolveEnvVarsInObject;
@@ -0,0 +1,46 @@
1
+ //#region src/config/env.ts
2
+ /**
3
+ * Resolve environment variable references in a string
4
+ *
5
+ * Syntax: ${VAR_NAME}
6
+ * Escape: \${VAR_NAME} (will not be resolved)
7
+ *
8
+ * @param value - String containing ${VAR} references
9
+ * @param options - Resolution options
10
+ * @returns String with env vars resolved
11
+ * @throws Error if env var is undefined and allowUndefined is false
12
+ */
13
+ function resolveEnvVars(value, options = {}) {
14
+ const { allowUndefined = false } = options;
15
+ if (!value) return value;
16
+ const ESCAPE_PLACEHOLDER = "\0ESCAPED_ENV\0";
17
+ let result = value.replace(/\\\$\{/g, ESCAPE_PLACEHOLDER);
18
+ result = result.replace(/\$\{([^}]+)\}/g, (_match, varName) => {
19
+ const envValue = process.env[varName];
20
+ if (envValue === void 0) {
21
+ if (allowUndefined) return "";
22
+ throw new Error(`Environment variable ${varName} is not defined`);
23
+ }
24
+ return envValue;
25
+ });
26
+ result = result.replace(new RegExp(ESCAPE_PLACEHOLDER, "g"), "${");
27
+ return result;
28
+ }
29
+ /**
30
+ * Resolve environment variables in all string fields of an object (deep)
31
+ */
32
+ function resolveEnvVarsInObject(obj, options = {}) {
33
+ if (obj === null || obj === void 0) return obj;
34
+ if (typeof obj === "string") return resolveEnvVars(obj, options);
35
+ if (Array.isArray(obj)) return obj.map((item) => resolveEnvVarsInObject(item, options));
36
+ if (typeof obj === "object") {
37
+ const result = {};
38
+ for (const [key, value] of Object.entries(obj)) result[key] = resolveEnvVarsInObject(value, options);
39
+ return result;
40
+ }
41
+ return obj;
42
+ }
43
+
44
+ //#endregion
45
+ export { resolveEnvVarsInObject };
46
+ //# sourceMappingURL=env.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.mjs","names":[],"sources":["../../src/config/env.ts"],"sourcesContent":["/**\n * Options for environment variable resolution\n */\nexport interface ResolveEnvOptions {\n /** If true, undefined env vars resolve to empty string instead of throwing */\n allowUndefined?: boolean;\n}\n\n/**\n * Resolve environment variable references in a string\n *\n * Syntax: ${VAR_NAME}\n * Escape: \\${VAR_NAME} (will not be resolved)\n *\n * @param value - String containing ${VAR} references\n * @param options - Resolution options\n * @returns String with env vars resolved\n * @throws Error if env var is undefined and allowUndefined is false\n */\nexport function resolveEnvVars(value: string, options: ResolveEnvOptions = {}): string {\n const { allowUndefined = false } = options;\n\n if (!value) {\n return value;\n }\n\n // First, handle escaped env vars (replace \\${ with a placeholder)\n const ESCAPE_PLACEHOLDER = \"\\x00ESCAPED_ENV\\x00\";\n let result = value.replace(/\\\\\\$\\{/g, ESCAPE_PLACEHOLDER);\n\n // Match ${VAR_NAME} pattern\n const envVarPattern = /\\$\\{([^}]+)\\}/g;\n\n result = result.replace(envVarPattern, (_match, varName: string) => {\n const envValue = process.env[varName];\n\n if (envValue === undefined) {\n if (allowUndefined) {\n return \"\";\n }\n throw new Error(`Environment variable ${varName} is not defined`);\n }\n\n return envValue;\n });\n\n // Restore escaped env vars\n result = result.replace(new RegExp(ESCAPE_PLACEHOLDER, \"g\"), \"${\");\n\n return result;\n}\n\n/**\n * Resolve environment variables in all string fields of an object (deep)\n */\nexport function resolveEnvVarsInObject<T>(obj: T, options: ResolveEnvOptions = {}): T {\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n if (typeof obj === \"string\") {\n return resolveEnvVars(obj, options) as T;\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => resolveEnvVarsInObject(item, options)) as T;\n }\n\n if (typeof obj === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[key] = resolveEnvVarsInObject(value, options);\n }\n return result as T;\n }\n\n return obj;\n}\n"],"mappings":";;;;;;;;;;;;AAmBA,SAAgB,eAAe,OAAe,UAA6B,EAAE,EAAU;CACrF,MAAM,EAAE,iBAAiB,UAAU;AAEnC,KAAI,CAAC,MACH,QAAO;CAIT,MAAM,qBAAqB;CAC3B,IAAI,SAAS,MAAM,QAAQ,WAAW,mBAAmB;AAKzD,UAAS,OAAO,QAFM,mBAEkB,QAAQ,YAAoB;EAClE,MAAM,WAAW,QAAQ,IAAI;AAE7B,MAAI,aAAa,QAAW;AAC1B,OAAI,eACF,QAAO;AAET,SAAM,IAAI,MAAM,wBAAwB,QAAQ,iBAAiB;;AAGnE,SAAO;GACP;AAGF,UAAS,OAAO,QAAQ,IAAI,OAAO,oBAAoB,IAAI,EAAE,KAAK;AAElE,QAAO;;;;;AAMT,SAAgB,uBAA0B,KAAQ,UAA6B,EAAE,EAAK;AACpF,KAAI,QAAQ,QAAQ,QAAQ,OAC1B,QAAO;AAGT,KAAI,OAAO,QAAQ,SACjB,QAAO,eAAe,KAAK,QAAQ;AAGrC,KAAI,MAAM,QAAQ,IAAI,CACpB,QAAO,IAAI,KAAK,SAAS,uBAAuB,MAAM,QAAQ,CAAC;AAGjE,KAAI,OAAO,QAAQ,UAAU;EAC3B,MAAM,SAAkC,EAAE;AAC1C,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,IAAI,CAC5C,QAAO,OAAO,uBAAuB,OAAO,QAAQ;AAEtD,SAAO;;AAGT,QAAO"}
@@ -0,0 +1,171 @@
1
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
2
+ const require_env = require('./env.cjs');
3
+ const require_schema = require('./schema.cjs');
4
+ let node_fs_promises = require("node:fs/promises");
5
+ let node_path = require("node:path");
6
+ let smol_toml = require("smol-toml");
7
+ let node_os = require("node:os");
8
+
9
+ //#region src/config/loader.ts
10
+ const CONFIG_DIR_NAME = ".afs-config";
11
+ const CONFIG_FILE_NAME = "config.toml";
12
+ /**
13
+ * Loads and merges AFS configuration from multiple layers
14
+ *
15
+ * Layer priority (lowest to highest):
16
+ * 1. User-level: ~/.afs-config/config.toml
17
+ * 2. All intermediate directories from project root to cwd
18
+ *
19
+ * Example: if cwd is /project/packages/cli, configs are merged from:
20
+ * ~/.afs-config/config.toml (user)
21
+ * /project/.afs-config/config.toml (project root, has .git)
22
+ * /project/packages/.afs-config/config.toml (intermediate)
23
+ * /project/packages/cli/.afs-config/config.toml (cwd)
24
+ */
25
+ var ConfigLoader = class {
26
+ userConfigDir;
27
+ constructor(options = {}) {
28
+ this.userConfigDir = options.userConfigDir ?? (0, node_path.join)((0, node_os.homedir)(), CONFIG_DIR_NAME);
29
+ }
30
+ /**
31
+ * Load and merge configuration from all layers
32
+ *
33
+ * @param cwd - Current working directory (defaults to process.cwd())
34
+ * @returns Merged configuration
35
+ * @throws Error on invalid config, TOML parse error, or duplicate mount paths
36
+ */
37
+ async load(cwd = process.cwd()) {
38
+ const configPaths = await this.getConfigPaths(cwd);
39
+ const configs = [];
40
+ for (const configPath of configPaths) {
41
+ const config = await this.loadSingleConfig(configPath);
42
+ configs.push(config);
43
+ }
44
+ return this.mergeConfigs(configs);
45
+ }
46
+ /**
47
+ * Get paths to all existing config files
48
+ *
49
+ * Collects configs from:
50
+ * 1. User-level: ~/.afs-config/config.toml
51
+ * 2. Project root (or topmost .afs-config dir) to cwd: all .afs-config/config.toml files
52
+ */
53
+ async getConfigPaths(cwd = process.cwd()) {
54
+ const paths = [];
55
+ const userConfigPath = (0, node_path.join)(this.userConfigDir, CONFIG_FILE_NAME);
56
+ if (await this.fileExists(userConfigPath)) paths.push(userConfigPath);
57
+ const startDir = await this.findProjectRoot(cwd) ?? await this.findTopmostAfsDir(cwd) ?? cwd;
58
+ const intermediatePaths = await this.collectConfigsFromTo(startDir, cwd);
59
+ paths.push(...intermediatePaths);
60
+ return paths;
61
+ }
62
+ /**
63
+ * Find the topmost directory containing .afs-config from startDir going up
64
+ */
65
+ async findTopmostAfsDir(startDir) {
66
+ let currentDir = startDir;
67
+ let topmostAfsDir = null;
68
+ while (true) {
69
+ if (await this.fileExists((0, node_path.join)(currentDir, CONFIG_DIR_NAME))) topmostAfsDir = currentDir;
70
+ const parentDir = (0, node_path.dirname)(currentDir);
71
+ if (parentDir === currentDir) break;
72
+ currentDir = parentDir;
73
+ }
74
+ return topmostAfsDir;
75
+ }
76
+ /**
77
+ * Collect all config files from startDir to endDir (inclusive)
78
+ * Returns paths in order from startDir to endDir (parent to child)
79
+ */
80
+ async collectConfigsFromTo(startDir, endDir) {
81
+ const paths = [];
82
+ const dirs = [];
83
+ let current = endDir;
84
+ while (true) {
85
+ dirs.unshift(current);
86
+ if (current === startDir) break;
87
+ const parent = (0, node_path.dirname)(current);
88
+ if (parent === current) break;
89
+ current = parent;
90
+ }
91
+ for (const dir of dirs) {
92
+ const configPath = (0, node_path.join)(dir, CONFIG_DIR_NAME, CONFIG_FILE_NAME);
93
+ if (await this.fileExists(configPath)) paths.push(configPath);
94
+ }
95
+ return paths;
96
+ }
97
+ /**
98
+ * Load a single config file
99
+ */
100
+ async loadSingleConfig(configPath) {
101
+ const content = await (0, node_fs_promises.readFile)(configPath, "utf-8");
102
+ let parsed;
103
+ try {
104
+ parsed = (0, smol_toml.parse)(content);
105
+ } catch (error) {
106
+ throw new Error(`Failed to parse TOML config at ${configPath}: ${error instanceof Error ? error.message : String(error)}`);
107
+ }
108
+ const resolved = require_env.resolveEnvVarsInObject(parsed);
109
+ const result = require_schema.ConfigSchema.safeParse(resolved);
110
+ if (!result.success) {
111
+ const errors = result.error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join("; ");
112
+ throw new Error(`Invalid config at ${configPath}: ${errors}`);
113
+ }
114
+ return result.data;
115
+ }
116
+ /**
117
+ * Merge multiple configs, checking for duplicate mount paths
118
+ * For serve config, later (more specific) configs override earlier ones
119
+ */
120
+ mergeConfigs(configs) {
121
+ const allMounts = [];
122
+ const seenPaths = /* @__PURE__ */ new Map();
123
+ let mergedServe;
124
+ for (const config of configs) {
125
+ for (const mount of config.mounts) {
126
+ if (seenPaths.has(mount.path)) throw new Error(`Duplicate mount path "${mount.path}" found in configuration. Mount paths must be unique across all config files.`);
127
+ seenPaths.set(mount.path, mount.uri);
128
+ allMounts.push(mount);
129
+ }
130
+ if (config.serve) mergedServe = mergedServe ? {
131
+ ...mergedServe,
132
+ ...config.serve
133
+ } : config.serve;
134
+ }
135
+ return {
136
+ mounts: allMounts,
137
+ serve: mergedServe
138
+ };
139
+ }
140
+ /**
141
+ * Find project root by looking for .git
142
+ * Note: Only .git is used as project root marker, not .afs-config,
143
+ * because .afs-config can exist at multiple levels for hierarchical config
144
+ */
145
+ async findProjectRoot(startDir) {
146
+ let currentDir = startDir;
147
+ while (true) {
148
+ if (await this.fileExists((0, node_path.join)(currentDir, ".git"))) return currentDir;
149
+ const parentDir = (0, node_path.dirname)(currentDir);
150
+ if (parentDir === currentDir) return null;
151
+ currentDir = parentDir;
152
+ }
153
+ }
154
+ /**
155
+ * Check if a file or directory exists
156
+ */
157
+ async fileExists(path) {
158
+ try {
159
+ await (0, node_fs_promises.access)(path);
160
+ return true;
161
+ } catch {
162
+ return false;
163
+ }
164
+ }
165
+ };
166
+ const configLoader = new ConfigLoader();
167
+
168
+ //#endregion
169
+ exports.CONFIG_DIR_NAME = CONFIG_DIR_NAME;
170
+ exports.CONFIG_FILE_NAME = CONFIG_FILE_NAME;
171
+ exports.ConfigLoader = ConfigLoader;
@@ -0,0 +1,169 @@
1
+ import { resolveEnvVarsInObject } from "./env.mjs";
2
+ import { ConfigSchema } from "./schema.mjs";
3
+ import { access, readFile } from "node:fs/promises";
4
+ import { dirname, join } from "node:path";
5
+ import { parse } from "smol-toml";
6
+ import { homedir } from "node:os";
7
+
8
+ //#region src/config/loader.ts
9
+ const CONFIG_DIR_NAME = ".afs-config";
10
+ const CONFIG_FILE_NAME = "config.toml";
11
+ /**
12
+ * Loads and merges AFS configuration from multiple layers
13
+ *
14
+ * Layer priority (lowest to highest):
15
+ * 1. User-level: ~/.afs-config/config.toml
16
+ * 2. All intermediate directories from project root to cwd
17
+ *
18
+ * Example: if cwd is /project/packages/cli, configs are merged from:
19
+ * ~/.afs-config/config.toml (user)
20
+ * /project/.afs-config/config.toml (project root, has .git)
21
+ * /project/packages/.afs-config/config.toml (intermediate)
22
+ * /project/packages/cli/.afs-config/config.toml (cwd)
23
+ */
24
+ var ConfigLoader = class {
25
+ userConfigDir;
26
+ constructor(options = {}) {
27
+ this.userConfigDir = options.userConfigDir ?? join(homedir(), CONFIG_DIR_NAME);
28
+ }
29
+ /**
30
+ * Load and merge configuration from all layers
31
+ *
32
+ * @param cwd - Current working directory (defaults to process.cwd())
33
+ * @returns Merged configuration
34
+ * @throws Error on invalid config, TOML parse error, or duplicate mount paths
35
+ */
36
+ async load(cwd = process.cwd()) {
37
+ const configPaths = await this.getConfigPaths(cwd);
38
+ const configs = [];
39
+ for (const configPath of configPaths) {
40
+ const config = await this.loadSingleConfig(configPath);
41
+ configs.push(config);
42
+ }
43
+ return this.mergeConfigs(configs);
44
+ }
45
+ /**
46
+ * Get paths to all existing config files
47
+ *
48
+ * Collects configs from:
49
+ * 1. User-level: ~/.afs-config/config.toml
50
+ * 2. Project root (or topmost .afs-config dir) to cwd: all .afs-config/config.toml files
51
+ */
52
+ async getConfigPaths(cwd = process.cwd()) {
53
+ const paths = [];
54
+ const userConfigPath = join(this.userConfigDir, CONFIG_FILE_NAME);
55
+ if (await this.fileExists(userConfigPath)) paths.push(userConfigPath);
56
+ const startDir = await this.findProjectRoot(cwd) ?? await this.findTopmostAfsDir(cwd) ?? cwd;
57
+ const intermediatePaths = await this.collectConfigsFromTo(startDir, cwd);
58
+ paths.push(...intermediatePaths);
59
+ return paths;
60
+ }
61
+ /**
62
+ * Find the topmost directory containing .afs-config from startDir going up
63
+ */
64
+ async findTopmostAfsDir(startDir) {
65
+ let currentDir = startDir;
66
+ let topmostAfsDir = null;
67
+ while (true) {
68
+ if (await this.fileExists(join(currentDir, CONFIG_DIR_NAME))) topmostAfsDir = currentDir;
69
+ const parentDir = dirname(currentDir);
70
+ if (parentDir === currentDir) break;
71
+ currentDir = parentDir;
72
+ }
73
+ return topmostAfsDir;
74
+ }
75
+ /**
76
+ * Collect all config files from startDir to endDir (inclusive)
77
+ * Returns paths in order from startDir to endDir (parent to child)
78
+ */
79
+ async collectConfigsFromTo(startDir, endDir) {
80
+ const paths = [];
81
+ const dirs = [];
82
+ let current = endDir;
83
+ while (true) {
84
+ dirs.unshift(current);
85
+ if (current === startDir) break;
86
+ const parent = dirname(current);
87
+ if (parent === current) break;
88
+ current = parent;
89
+ }
90
+ for (const dir of dirs) {
91
+ const configPath = join(dir, CONFIG_DIR_NAME, CONFIG_FILE_NAME);
92
+ if (await this.fileExists(configPath)) paths.push(configPath);
93
+ }
94
+ return paths;
95
+ }
96
+ /**
97
+ * Load a single config file
98
+ */
99
+ async loadSingleConfig(configPath) {
100
+ const content = await readFile(configPath, "utf-8");
101
+ let parsed;
102
+ try {
103
+ parsed = parse(content);
104
+ } catch (error) {
105
+ throw new Error(`Failed to parse TOML config at ${configPath}: ${error instanceof Error ? error.message : String(error)}`);
106
+ }
107
+ const resolved = resolveEnvVarsInObject(parsed);
108
+ const result = ConfigSchema.safeParse(resolved);
109
+ if (!result.success) {
110
+ const errors = result.error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join("; ");
111
+ throw new Error(`Invalid config at ${configPath}: ${errors}`);
112
+ }
113
+ return result.data;
114
+ }
115
+ /**
116
+ * Merge multiple configs, checking for duplicate mount paths
117
+ * For serve config, later (more specific) configs override earlier ones
118
+ */
119
+ mergeConfigs(configs) {
120
+ const allMounts = [];
121
+ const seenPaths = /* @__PURE__ */ new Map();
122
+ let mergedServe;
123
+ for (const config of configs) {
124
+ for (const mount of config.mounts) {
125
+ if (seenPaths.has(mount.path)) throw new Error(`Duplicate mount path "${mount.path}" found in configuration. Mount paths must be unique across all config files.`);
126
+ seenPaths.set(mount.path, mount.uri);
127
+ allMounts.push(mount);
128
+ }
129
+ if (config.serve) mergedServe = mergedServe ? {
130
+ ...mergedServe,
131
+ ...config.serve
132
+ } : config.serve;
133
+ }
134
+ return {
135
+ mounts: allMounts,
136
+ serve: mergedServe
137
+ };
138
+ }
139
+ /**
140
+ * Find project root by looking for .git
141
+ * Note: Only .git is used as project root marker, not .afs-config,
142
+ * because .afs-config can exist at multiple levels for hierarchical config
143
+ */
144
+ async findProjectRoot(startDir) {
145
+ let currentDir = startDir;
146
+ while (true) {
147
+ if (await this.fileExists(join(currentDir, ".git"))) return currentDir;
148
+ const parentDir = dirname(currentDir);
149
+ if (parentDir === currentDir) return null;
150
+ currentDir = parentDir;
151
+ }
152
+ }
153
+ /**
154
+ * Check if a file or directory exists
155
+ */
156
+ async fileExists(path) {
157
+ try {
158
+ await access(path);
159
+ return true;
160
+ } catch {
161
+ return false;
162
+ }
163
+ }
164
+ };
165
+ const configLoader = new ConfigLoader();
166
+
167
+ //#endregion
168
+ export { CONFIG_DIR_NAME, CONFIG_FILE_NAME, ConfigLoader };
169
+ //# sourceMappingURL=loader.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.mjs","names":[],"sources":["../../src/config/loader.ts"],"sourcesContent":["import { access, readFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { parse } from \"smol-toml\";\nimport { resolveEnvVarsInObject } from \"./env.js\";\nimport { type AFSConfig, ConfigSchema, type MountConfig, type ServeConfig } from \"./schema.js\";\n\nexport const CONFIG_DIR_NAME = \".afs-config\";\nexport const CONFIG_FILE_NAME = \"config.toml\";\n\nexport interface ConfigLoaderOptions {\n /** Custom path to user-level config directory (for testing) */\n userConfigDir?: string;\n}\n\n/**\n * Loads and merges AFS configuration from multiple layers\n *\n * Layer priority (lowest to highest):\n * 1. User-level: ~/.afs-config/config.toml\n * 2. All intermediate directories from project root to cwd\n *\n * Example: if cwd is /project/packages/cli, configs are merged from:\n * ~/.afs-config/config.toml (user)\n * /project/.afs-config/config.toml (project root, has .git)\n * /project/packages/.afs-config/config.toml (intermediate)\n * /project/packages/cli/.afs-config/config.toml (cwd)\n */\nexport class ConfigLoader {\n private userConfigDir: string;\n\n constructor(options: ConfigLoaderOptions = {}) {\n this.userConfigDir = options.userConfigDir ?? join(homedir(), CONFIG_DIR_NAME);\n }\n\n /**\n * Load and merge configuration from all layers\n *\n * @param cwd - Current working directory (defaults to process.cwd())\n * @returns Merged configuration\n * @throws Error on invalid config, TOML parse error, or duplicate mount paths\n */\n async load(cwd: string = process.cwd()): Promise<AFSConfig> {\n const configPaths = await this.getConfigPaths(cwd);\n const configs: AFSConfig[] = [];\n\n for (const configPath of configPaths) {\n const config = await this.loadSingleConfig(configPath);\n configs.push(config);\n }\n\n return this.mergeConfigs(configs);\n }\n\n /**\n * Get paths to all existing config files\n *\n * Collects configs from:\n * 1. User-level: ~/.afs-config/config.toml\n * 2. Project root (or topmost .afs-config dir) to cwd: all .afs-config/config.toml files\n */\n async getConfigPaths(cwd: string = process.cwd()): Promise<string[]> {\n const paths: string[] = [];\n\n // 1. User-level config\n const userConfigPath = join(this.userConfigDir, CONFIG_FILE_NAME);\n if (await this.fileExists(userConfigPath)) {\n paths.push(userConfigPath);\n }\n\n // 2. Find project root (look for .git going up)\n const projectRoot = await this.findProjectRoot(cwd);\n\n // 3. Determine start directory\n // If project root found, use it; otherwise find topmost .afs-config directory\n const startDir = projectRoot ?? (await this.findTopmostAfsDir(cwd)) ?? cwd;\n\n // 4. Collect all config files from start to cwd\n const intermediatePaths = await this.collectConfigsFromTo(startDir, cwd);\n paths.push(...intermediatePaths);\n\n return paths;\n }\n\n /**\n * Find the topmost directory containing .afs-config from startDir going up\n */\n private async findTopmostAfsDir(startDir: string): Promise<string | null> {\n let currentDir = startDir;\n let topmostAfsDir: string | null = null;\n\n while (true) {\n if (await this.fileExists(join(currentDir, CONFIG_DIR_NAME))) {\n topmostAfsDir = currentDir;\n }\n\n const parentDir = dirname(currentDir);\n if (parentDir === currentDir) {\n // Reached filesystem root\n break;\n }\n currentDir = parentDir;\n }\n\n return topmostAfsDir;\n }\n\n /**\n * Collect all config files from startDir to endDir (inclusive)\n * Returns paths in order from startDir to endDir (parent to child)\n */\n private async collectConfigsFromTo(startDir: string, endDir: string): Promise<string[]> {\n const paths: string[] = [];\n\n // Build list of directories from startDir to endDir\n const dirs: string[] = [];\n let current = endDir;\n\n while (true) {\n dirs.unshift(current); // prepend to maintain parent-to-child order\n\n if (current === startDir) {\n break;\n }\n\n const parent = dirname(current);\n if (parent === current) {\n // Reached filesystem root without finding startDir\n // This shouldn't happen if startDir is an ancestor of endDir\n break;\n }\n current = parent;\n }\n\n // Check each directory for config file\n for (const dir of dirs) {\n const configPath = join(dir, CONFIG_DIR_NAME, CONFIG_FILE_NAME);\n if (await this.fileExists(configPath)) {\n paths.push(configPath);\n }\n }\n\n return paths;\n }\n\n /**\n * Load a single config file\n */\n private async loadSingleConfig(configPath: string): Promise<AFSConfig> {\n const content = await readFile(configPath, \"utf-8\");\n\n let parsed: unknown;\n try {\n parsed = parse(content);\n } catch (error) {\n throw new Error(\n `Failed to parse TOML config at ${configPath}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n // Resolve environment variables\n const resolved = resolveEnvVarsInObject(parsed);\n\n // Validate against schema\n const result = ConfigSchema.safeParse(resolved);\n if (!result.success) {\n const errors = result.error.errors.map((e) => `${e.path.join(\".\")}: ${e.message}`).join(\"; \");\n throw new Error(`Invalid config at ${configPath}: ${errors}`);\n }\n\n return result.data;\n }\n\n /**\n * Merge multiple configs, checking for duplicate mount paths\n * For serve config, later (more specific) configs override earlier ones\n */\n private mergeConfigs(configs: AFSConfig[]): AFSConfig {\n const allMounts: MountConfig[] = [];\n const seenPaths = new Map<string, string>(); // path -> source file info\n let mergedServe: ServeConfig | undefined;\n\n for (const config of configs) {\n // Merge mounts\n for (const mount of config.mounts) {\n if (seenPaths.has(mount.path)) {\n throw new Error(\n `Duplicate mount path \"${mount.path}\" found in configuration. ` +\n `Mount paths must be unique across all config files.`,\n );\n }\n seenPaths.set(mount.path, mount.uri);\n allMounts.push(mount);\n }\n\n // Merge serve config (later configs override earlier ones)\n if (config.serve) {\n mergedServe = mergedServe ? { ...mergedServe, ...config.serve } : config.serve;\n }\n }\n\n return { mounts: allMounts, serve: mergedServe };\n }\n\n /**\n * Find project root by looking for .git\n * Note: Only .git is used as project root marker, not .afs-config,\n * because .afs-config can exist at multiple levels for hierarchical config\n */\n private async findProjectRoot(startDir: string): Promise<string | null> {\n let currentDir = startDir;\n\n while (true) {\n // Check for .git directory\n if (await this.fileExists(join(currentDir, \".git\"))) {\n return currentDir;\n }\n\n const parentDir = dirname(currentDir);\n if (parentDir === currentDir) {\n // Reached filesystem root\n return null;\n }\n currentDir = parentDir;\n }\n }\n\n /**\n * Check if a file or directory exists\n */\n private async fileExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n }\n}\n\n// Default singleton instance\nexport const configLoader = new ConfigLoader();\n"],"mappings":"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"}
@@ -0,0 +1,92 @@
1
+ const require_uri_parser = require('./uri-parser.cjs');
2
+
3
+ //#region src/config/provider-factory.ts
4
+ /**
5
+ * Create an AFS provider from a mount configuration
6
+ *
7
+ * @param mount - Mount configuration with URI and options
8
+ * @returns Provider instance
9
+ * @throws Error if scheme is unknown or not implemented
10
+ */
11
+ async function createProvider(mount) {
12
+ const parsed = require_uri_parser.parseURI(mount.uri);
13
+ switch (parsed.scheme) {
14
+ case "fs": return createAFSFSProvider(mount, parsed.path);
15
+ case "git": return createGitProvider(mount, parsed.path, parsed.params, parsed.host);
16
+ case "sqlite": return createSQLiteProvider(mount, parsed.path);
17
+ case "json": return createJSONProvider(mount, parsed.path);
18
+ case "http":
19
+ case "https": return createHttpProvider(mount);
20
+ default: throw new Error(`Unknown URI scheme: ${parsed.scheme}`);
21
+ }
22
+ }
23
+ async function createAFSFSProvider(mount, localPath) {
24
+ const { AFSFS } = await import("@aigne/afs-fs");
25
+ return new AFSFS({
26
+ localPath,
27
+ name: mount.path.slice(1).replace(/\//g, "-") || "fs",
28
+ description: mount.description,
29
+ accessMode: mount.access_mode,
30
+ ...mount.options
31
+ });
32
+ }
33
+ async function createGitProvider(mount, repoPath, params, host) {
34
+ const { AFSGit } = await import("@aigne/afs-git");
35
+ if (host) return new AFSGit({
36
+ remoteUrl: `git@${host}:${repoPath}`,
37
+ name: mount.path.slice(1).replace(/\//g, "-") || "git",
38
+ description: mount.description,
39
+ accessMode: mount.access_mode ?? "readonly",
40
+ branches: params.branch ? [params.branch] : void 0,
41
+ ...mount.options
42
+ });
43
+ if (repoPath.startsWith("https://") || repoPath.startsWith("http://")) return new AFSGit({
44
+ remoteUrl: repoPath,
45
+ name: mount.path.slice(1).replace(/\//g, "-") || "git",
46
+ description: mount.description,
47
+ accessMode: mount.access_mode ?? "readonly",
48
+ branches: params.branch ? [params.branch] : void 0,
49
+ ...mount.options
50
+ });
51
+ return new AFSGit({
52
+ repoPath,
53
+ name: mount.path.slice(1).replace(/\//g, "-") || "git",
54
+ description: mount.description,
55
+ accessMode: mount.access_mode ?? "readonly",
56
+ branches: params.branch ? [params.branch] : void 0,
57
+ ...mount.options
58
+ });
59
+ }
60
+ async function createSQLiteProvider(mount, dbPath) {
61
+ const { SQLiteAFS } = await import("@aigne/afs-sqlite");
62
+ return new SQLiteAFS({
63
+ url: `file:${dbPath}`,
64
+ name: mount.path.slice(1).replace(/\//g, "-") || "sqlite",
65
+ description: mount.description,
66
+ accessMode: mount.access_mode,
67
+ ...mount.options
68
+ });
69
+ }
70
+ async function createJSONProvider(mount, jsonPath) {
71
+ const { AFSJSON } = await import("@aigne/afs-json");
72
+ return new AFSJSON({
73
+ jsonPath,
74
+ name: mount.path.slice(1).replace(/\//g, "-") || "json",
75
+ description: mount.description,
76
+ accessMode: mount.access_mode,
77
+ ...mount.options
78
+ });
79
+ }
80
+ async function createHttpProvider(mount) {
81
+ const { AFSHttpClient } = await import("@aigne/afs-http");
82
+ return new AFSHttpClient({
83
+ url: mount.uri,
84
+ name: mount.path.slice(1).replace(/\//g, "-") || "http",
85
+ description: mount.description,
86
+ accessMode: mount.access_mode,
87
+ ...mount.options
88
+ });
89
+ }
90
+
91
+ //#endregion
92
+ exports.createProvider = createProvider;