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

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 (70) hide show
  1. package/README.md +13 -9
  2. package/dist/_virtual/rolldown_runtime.cjs +29 -0
  3. package/dist/cli.cjs +215 -0
  4. package/dist/cli.d.cts +1 -0
  5. package/dist/cli.mjs +205 -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 +7 -0
  14. package/dist/commands/index.mjs +9 -0
  15. package/dist/commands/ls.cjs +136 -0
  16. package/dist/commands/ls.mjs +135 -0
  17. package/dist/commands/ls.mjs.map +1 -0
  18. package/dist/commands/mount.cjs +157 -0
  19. package/dist/commands/mount.mjs +153 -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/stat.cjs +113 -0
  25. package/dist/commands/stat.mjs +112 -0
  26. package/dist/commands/stat.mjs.map +1 -0
  27. package/dist/commands/write.cjs +52 -0
  28. package/dist/commands/write.mjs +51 -0
  29. package/dist/commands/write.mjs.map +1 -0
  30. package/dist/config/env.cjs +46 -0
  31. package/dist/config/env.mjs +46 -0
  32. package/dist/config/env.mjs.map +1 -0
  33. package/dist/config/loader.cjs +160 -0
  34. package/dist/config/loader.mjs +158 -0
  35. package/dist/config/loader.mjs.map +1 -0
  36. package/dist/config/provider-factory.cjs +74 -0
  37. package/dist/config/provider-factory.mjs +75 -0
  38. package/dist/config/provider-factory.mjs.map +1 -0
  39. package/dist/config/schema.cjs +22 -0
  40. package/dist/config/schema.mjs +22 -0
  41. package/dist/config/schema.mjs.map +1 -0
  42. package/dist/config/uri-parser.cjs +75 -0
  43. package/dist/config/uri-parser.mjs +75 -0
  44. package/dist/config/uri-parser.mjs.map +1 -0
  45. package/dist/errors.cjs +29 -0
  46. package/dist/errors.mjs +28 -0
  47. package/dist/errors.mjs.map +1 -0
  48. package/dist/index.cjs +3 -0
  49. package/dist/index.d.cts +2 -0
  50. package/dist/index.d.mts +1 -3
  51. package/dist/index.mjs +1 -1
  52. package/dist/runtime.cjs +82 -0
  53. package/dist/runtime.mjs +82 -0
  54. package/dist/runtime.mjs.map +1 -0
  55. package/dist/version.cjs +9 -0
  56. package/dist/version.d.cts +5 -0
  57. package/dist/version.d.cts.map +1 -0
  58. package/dist/version.d.mts +5 -0
  59. package/dist/version.d.mts.map +1 -0
  60. package/dist/version.mjs +9 -0
  61. package/dist/version.mjs.map +1 -0
  62. package/package.json +51 -11
  63. package/.turbo/turbo-build.log +0 -18
  64. package/.turbo/turbo-check-types.log +0 -4
  65. package/dist/version--p6A8sKX.mjs +0 -5
  66. package/src/cli.test.ts +0 -8
  67. package/src/cli.ts +0 -29
  68. package/src/index.ts +0 -7
  69. package/src/version.ts +0 -1
  70. package/tsconfig.json +0 -16
@@ -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":"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"}
@@ -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,160 @@
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
+ */
119
+ mergeConfigs(configs) {
120
+ const allMounts = [];
121
+ const seenPaths = /* @__PURE__ */ new Map();
122
+ for (const config of configs) for (const mount of config.mounts) {
123
+ 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.`);
124
+ seenPaths.set(mount.path, mount.uri);
125
+ allMounts.push(mount);
126
+ }
127
+ return { mounts: allMounts };
128
+ }
129
+ /**
130
+ * Find project root by looking for .git
131
+ * Note: Only .git is used as project root marker, not .afs-config,
132
+ * because .afs-config can exist at multiple levels for hierarchical config
133
+ */
134
+ async findProjectRoot(startDir) {
135
+ let currentDir = startDir;
136
+ while (true) {
137
+ if (await this.fileExists((0, node_path.join)(currentDir, ".git"))) return currentDir;
138
+ const parentDir = (0, node_path.dirname)(currentDir);
139
+ if (parentDir === currentDir) return null;
140
+ currentDir = parentDir;
141
+ }
142
+ }
143
+ /**
144
+ * Check if a file or directory exists
145
+ */
146
+ async fileExists(path) {
147
+ try {
148
+ await (0, node_fs_promises.access)(path);
149
+ return true;
150
+ } catch {
151
+ return false;
152
+ }
153
+ }
154
+ };
155
+ const configLoader = new ConfigLoader();
156
+
157
+ //#endregion
158
+ exports.CONFIG_DIR_NAME = CONFIG_DIR_NAME;
159
+ exports.CONFIG_FILE_NAME = CONFIG_FILE_NAME;
160
+ exports.ConfigLoader = ConfigLoader;