@aigne/afs-cli 1.11.0-beta.5 → 1.11.0-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/cli.cjs +41 -18
  2. package/dist/cli.mjs +42 -19
  3. package/dist/cli.mjs.map +1 -1
  4. package/dist/commands/exec.cjs +132 -14
  5. package/dist/commands/exec.mjs +129 -14
  6. package/dist/commands/exec.mjs.map +1 -1
  7. package/dist/commands/explain.cjs +1 -1
  8. package/dist/commands/explain.mjs +1 -1
  9. package/dist/commands/explain.mjs.map +1 -1
  10. package/dist/commands/index.mjs +1 -1
  11. package/dist/commands/ls.cjs +129 -30
  12. package/dist/commands/ls.mjs +129 -30
  13. package/dist/commands/ls.mjs.map +1 -1
  14. package/dist/commands/read.cjs +213 -14
  15. package/dist/commands/read.mjs +213 -14
  16. package/dist/commands/read.mjs.map +1 -1
  17. package/dist/commands/stat.cjs +116 -34
  18. package/dist/commands/stat.mjs +117 -34
  19. package/dist/commands/stat.mjs.map +1 -1
  20. package/dist/commands/write.cjs +37 -4
  21. package/dist/commands/write.mjs +38 -4
  22. package/dist/commands/write.mjs.map +1 -1
  23. package/dist/config/loader.cjs +12 -1
  24. package/dist/config/loader.mjs +12 -1
  25. package/dist/config/loader.mjs.map +1 -1
  26. package/dist/config/provider-factory.cjs +310 -3
  27. package/dist/config/provider-factory.mjs +310 -3
  28. package/dist/config/provider-factory.mjs.map +1 -1
  29. package/dist/config/uri-parser.cjs +195 -2
  30. package/dist/config/uri-parser.mjs +195 -2
  31. package/dist/config/uri-parser.mjs.map +1 -1
  32. package/dist/explorer/actions.cjs +53 -23
  33. package/dist/explorer/actions.mjs +54 -23
  34. package/dist/explorer/actions.mjs.map +1 -1
  35. package/dist/explorer/components/dialog.cjs +163 -10
  36. package/dist/explorer/components/dialog.mjs +163 -10
  37. package/dist/explorer/components/dialog.mjs.map +1 -1
  38. package/dist/explorer/components/file-list.mjs.map +1 -1
  39. package/dist/explorer/components/metadata-panel.cjs +39 -25
  40. package/dist/explorer/components/metadata-panel.mjs +39 -25
  41. package/dist/explorer/components/metadata-panel.mjs.map +1 -1
  42. package/dist/explorer/screen.cjs +23 -8
  43. package/dist/explorer/screen.mjs +24 -9
  44. package/dist/explorer/screen.mjs.map +1 -1
  45. package/dist/explorer/theme.cjs +3 -1
  46. package/dist/explorer/theme.mjs +3 -1
  47. package/dist/explorer/theme.mjs.map +1 -1
  48. package/dist/path-utils.cjs +2 -1
  49. package/dist/path-utils.mjs +1 -1
  50. package/dist/runtime.cjs +24 -0
  51. package/dist/runtime.mjs +24 -0
  52. package/dist/runtime.mjs.map +1 -1
  53. package/dist/ui/header.cjs +0 -9
  54. package/dist/ui/header.mjs +1 -9
  55. package/dist/ui/header.mjs.map +1 -1
  56. package/dist/ui/index.cjs +0 -2
  57. package/dist/ui/index.mjs +2 -3
  58. package/dist/ui/index.mjs.map +1 -1
  59. package/dist/utils/meta.cjs +51 -0
  60. package/dist/utils/meta.mjs +49 -0
  61. package/dist/utils/meta.mjs.map +1 -0
  62. package/package.json +19 -9
@@ -10,7 +10,7 @@ function formatMetadata(entry, metadata) {
10
10
  const prefix = icon ? `${icon} ` : "";
11
11
  lines.push(`${prefix}${entry.name}`);
12
12
  lines.push("");
13
- lines.push(`Type: ${entry.type}`);
13
+ lines.push(`Path: ${entry.path}`);
14
14
  if (entry.size !== void 0 || metadata?.size !== void 0) {
15
15
  const size = metadata?.size ?? entry.size;
16
16
  lines.push(`Size: ${formatSize(size)}`);
@@ -29,20 +29,48 @@ function formatMetadata(entry, metadata) {
29
29
  const display = hash.length > 20 ? `${hash.slice(0, 20)}...` : hash;
30
30
  lines.push(`Hash: ${display}`);
31
31
  }
32
- if (entry.description || metadata?.description) {
33
- lines.push("");
34
- lines.push("Description:");
35
- const wrapped = wrapText(metadata?.description ?? entry.description, 25);
36
- lines.push(...wrapped);
37
- }
38
- if (metadata?.mountPath) {
39
- lines.push("");
40
- lines.push(`Mount: ${metadata.mountPath}`);
41
- }
32
+ if (metadata?.mountPath) lines.push(`Mount: ${metadata.mountPath}`);
42
33
  if (metadata?.uri) lines.push(`URI: ${metadata.uri}`);
34
+ if (metadata?.extra && Object.keys(metadata.extra).length > 0) {
35
+ const builtInFields = new Set([
36
+ "size",
37
+ "mimeType",
38
+ "childrenCount",
39
+ "hash",
40
+ "provider",
41
+ "mountPath",
42
+ "uri",
43
+ "permissions"
44
+ ]);
45
+ for (const [key, value] of Object.entries(metadata.extra)) {
46
+ if (builtInFields.has(key) || value === void 0) continue;
47
+ const displayKey = key.charAt(0).toUpperCase() + key.slice(1);
48
+ const displayValue = formatValue(value);
49
+ if (displayValue.includes("\n")) {
50
+ lines.push("");
51
+ lines.push(`${displayKey}:`);
52
+ for (const line of displayValue.split("\n")) lines.push(` ${line}`);
53
+ } else lines.push(`${displayKey}: ${displayValue}`);
54
+ }
55
+ }
43
56
  return lines;
44
57
  }
45
58
  /**
59
+ * Format a value for display
60
+ */
61
+ function formatValue(value) {
62
+ if (value === null || value === void 0) return "";
63
+ if (typeof value === "string") return value;
64
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
65
+ if (Array.isArray(value)) {
66
+ if (value.length === 0) return "[]";
67
+ if (value.every((v) => typeof v === "string" || typeof v === "number")) return value.join(", ");
68
+ return JSON.stringify(value, null, 2);
69
+ }
70
+ if (typeof value === "object") return JSON.stringify(value, null, 2);
71
+ return String(value);
72
+ }
73
+ /**
46
74
  * Format date and time
47
75
  */
48
76
  function formatDateTime(date) {
@@ -55,20 +83,6 @@ function formatDateTime(date) {
55
83
  });
56
84
  }
57
85
  /**
58
- * Word wrap text
59
- */
60
- function wrapText(text, maxWidth) {
61
- const words = text.split(/\s+/);
62
- const lines = [];
63
- let current = "";
64
- for (const word of words) if (current.length + word.length + 1 > maxWidth) {
65
- if (current) lines.push(current);
66
- current = word;
67
- } else current = current ? `${current} ${word}` : word;
68
- if (current) lines.push(current);
69
- return lines;
70
- }
71
- /**
72
86
  * Create metadata panel component
73
87
  */
74
88
  function createMetadataPanel(blessed, options) {
@@ -1 +1 @@
1
- {"version":3,"file":"metadata-panel.mjs","names":[],"sources":["../../../src/explorer/components/metadata-panel.ts"],"sourcesContent":["/**\n * AFS Explorer Metadata Panel Component\n *\n * Right panel showing detailed metadata for selected entry.\n */\n\nimport type Blessed from \"blessed\";\nimport { Colors, formatSize, Icons } from \"../theme.js\";\nimport type { EntryMetadata, ExplorerEntry } from \"../types.js\";\n\nexport interface MetadataPanelOptions {\n parent: Blessed.Widgets.Node;\n width: string | number;\n height: string | number;\n top?: string | number;\n right?: string | number;\n}\n\n/**\n * Format metadata for display\n */\nexport function formatMetadata(entry: ExplorerEntry, metadata?: EntryMetadata): string[] {\n const lines: string[] = [];\n\n // Entry name with type indicator (only show icon for non-files)\n const icon = entry.type !== \"file\" ? Icons[entry.type] : \"\";\n const prefix = icon ? `${icon} ` : \"\";\n lines.push(`${prefix}${entry.name}`);\n lines.push(\"\");\n\n // Type\n lines.push(`Type: ${entry.type}`);\n\n // Size\n if (entry.size !== undefined || metadata?.size !== undefined) {\n const size = metadata?.size ?? entry.size;\n lines.push(`Size: ${formatSize(size!)}`);\n }\n\n // Children count\n if (entry.childrenCount !== undefined || metadata?.childrenCount !== undefined) {\n const count = metadata?.childrenCount ?? entry.childrenCount;\n lines.push(`Items: ${count}`);\n }\n\n // Modified date\n if (entry.modified || metadata?.modified) {\n const date = metadata?.modified ?? entry.modified;\n lines.push(`Modified: ${formatDateTime(date!)}`);\n }\n\n // Provider\n if (entry.provider || metadata?.provider) {\n lines.push(`Provider: ${metadata?.provider ?? entry.provider}`);\n }\n\n // Hash\n if (entry.hash || metadata?.hash) {\n const hash = metadata?.hash ?? entry.hash;\n // Truncate long hashes\n const display = hash!.length > 20 ? `${hash!.slice(0, 20)}...` : hash;\n lines.push(`Hash: ${display}`);\n }\n\n // Description\n if (entry.description || metadata?.description) {\n lines.push(\"\");\n lines.push(\"Description:\");\n const desc = metadata?.description ?? entry.description;\n // Word wrap description\n const wrapped = wrapText(desc!, 25);\n lines.push(...wrapped);\n }\n\n // Mount path\n if (metadata?.mountPath) {\n lines.push(\"\");\n lines.push(`Mount: ${metadata.mountPath}`);\n }\n\n // URI\n if (metadata?.uri) {\n lines.push(`URI: ${metadata.uri}`);\n }\n\n return lines;\n}\n\n/**\n * Format date and time\n */\nfunction formatDateTime(date: Date): string {\n return date.toLocaleString(\"en\", {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n}\n\n/**\n * Word wrap text\n */\nfunction wrapText(text: string, maxWidth: number): string[] {\n const words = text.split(/\\s+/);\n const lines: string[] = [];\n let current = \"\";\n\n for (const word of words) {\n if (current.length + word.length + 1 > maxWidth) {\n if (current) lines.push(current);\n current = word;\n } else {\n current = current ? `${current} ${word}` : word;\n }\n }\n if (current) lines.push(current);\n\n return lines;\n}\n\n/**\n * Create metadata panel component\n */\nexport function createMetadataPanel(blessed: typeof Blessed, options: MetadataPanelOptions) {\n const { parent, width, height, top = 0, right = 0 } = options;\n\n // Create box for metadata panel\n const panel = blessed.box({\n parent,\n top,\n right,\n width,\n height,\n tags: true,\n scrollable: true,\n alwaysScroll: true,\n style: {\n fg: Colors.fg.normal,\n bg: Colors.bg.main,\n },\n border: {\n type: \"line\",\n },\n label: \" Details \",\n });\n\n let currentEntry: ExplorerEntry | undefined;\n let _currentMetadata: EntryMetadata | undefined;\n\n return {\n element: panel,\n\n /**\n * Update panel with entry info\n */\n update(entry: ExplorerEntry, metadata?: EntryMetadata): void {\n currentEntry = entry;\n _currentMetadata = metadata;\n\n if (entry.type === \"up\") {\n panel.setContent(\" Parent directory\");\n (panel.screen as Blessed.Widgets.Screen)?.render();\n return;\n }\n\n const lines = formatMetadata(entry, metadata);\n panel.setContent(lines.map((l) => ` ${l}`).join(\"\\n\"));\n (panel.screen as Blessed.Widgets.Screen)?.render();\n },\n\n /**\n * Clear the panel\n */\n clear(): void {\n currentEntry = undefined;\n _currentMetadata = undefined;\n panel.setContent(\"\");\n (panel.screen as Blessed.Widgets.Screen)?.render();\n },\n\n /**\n * Get current entry\n */\n getCurrentEntry(): ExplorerEntry | undefined {\n return currentEntry;\n },\n\n /**\n * Destroy the component\n */\n destroy(): void {\n panel.destroy();\n },\n };\n}\n\nexport type MetadataPanel = ReturnType<typeof createMetadataPanel>;\n"],"mappings":"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"}
1
+ {"version":3,"file":"metadata-panel.mjs","names":[],"sources":["../../../src/explorer/components/metadata-panel.ts"],"sourcesContent":["/**\n * AFS Explorer Metadata Panel Component\n *\n * Right panel showing detailed metadata for selected entry.\n */\n\nimport type Blessed from \"blessed\";\nimport { Colors, formatSize, Icons } from \"../theme.js\";\nimport type { EntryMetadata, ExplorerEntry } from \"../types.js\";\n\nexport interface MetadataPanelOptions {\n parent: Blessed.Widgets.Node;\n width: string | number;\n height: string | number;\n top?: string | number;\n right?: string | number;\n}\n\n/**\n * Format metadata for display\n */\nexport function formatMetadata(entry: ExplorerEntry, metadata?: EntryMetadata): string[] {\n const lines: string[] = [];\n\n // Entry name with type indicator (only show icon for non-files)\n const icon = entry.type !== \"file\" ? Icons[entry.type] : \"\";\n const prefix = icon ? `${icon} ` : \"\";\n lines.push(`${prefix}${entry.name}`);\n lines.push(\"\");\n\n // Path\n lines.push(`Path: ${entry.path}`);\n\n // Size\n if (entry.size !== undefined || metadata?.size !== undefined) {\n const size = metadata?.size ?? entry.size;\n lines.push(`Size: ${formatSize(size!)}`);\n }\n\n // Children count\n if (entry.childrenCount !== undefined || metadata?.childrenCount !== undefined) {\n const count = metadata?.childrenCount ?? entry.childrenCount;\n lines.push(`Items: ${count}`);\n }\n\n // Modified date\n if (entry.modified || metadata?.modified) {\n const date = metadata?.modified ?? entry.modified;\n lines.push(`Modified: ${formatDateTime(date!)}`);\n }\n\n // Provider\n if (entry.provider || metadata?.provider) {\n lines.push(`Provider: ${metadata?.provider ?? entry.provider}`);\n }\n\n // Hash\n if (entry.hash || metadata?.hash) {\n const hash = metadata?.hash ?? entry.hash;\n // Truncate long hashes\n const display = hash!.length > 20 ? `${hash!.slice(0, 20)}...` : hash;\n lines.push(`Hash: ${display}`);\n }\n\n // Mount path\n if (metadata?.mountPath) {\n lines.push(`Mount: ${metadata.mountPath}`);\n }\n\n // URI\n if (metadata?.uri) {\n lines.push(`URI: ${metadata.uri}`);\n }\n\n // Display all meta fields from extra (flat, same level as other fields)\n if (metadata?.extra && Object.keys(metadata.extra).length > 0) {\n // Filter out built-in fields that are already displayed above\n const builtInFields = new Set([\n \"size\",\n \"mimeType\",\n \"childrenCount\",\n \"hash\",\n \"provider\",\n \"mountPath\",\n \"uri\",\n \"permissions\",\n ]);\n\n for (const [key, value] of Object.entries(metadata.extra)) {\n if (builtInFields.has(key) || value === undefined) continue;\n\n // Capitalize first letter of key for display\n const displayKey = key.charAt(0).toUpperCase() + key.slice(1);\n const displayValue = formatValue(value);\n\n if (displayValue.includes(\"\\n\")) {\n lines.push(\"\");\n lines.push(`${displayKey}:`);\n for (const line of displayValue.split(\"\\n\")) {\n lines.push(` ${line}`);\n }\n } else {\n lines.push(`${displayKey}: ${displayValue}`);\n }\n }\n }\n\n return lines;\n}\n\n/**\n * Format a value for display\n */\nfunction formatValue(value: unknown): string {\n if (value === null || value === undefined) {\n return \"\";\n }\n if (typeof value === \"string\") {\n return value;\n }\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n if (Array.isArray(value)) {\n if (value.length === 0) return \"[]\";\n if (value.every((v) => typeof v === \"string\" || typeof v === \"number\")) {\n return value.join(\", \");\n }\n return JSON.stringify(value, null, 2);\n }\n if (typeof value === \"object\") {\n return JSON.stringify(value, null, 2);\n }\n return String(value);\n}\n\n/**\n * Format date and time\n */\nfunction formatDateTime(date: Date): string {\n return date.toLocaleString(\"en\", {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n}\n\n/**\n * Create metadata panel component\n */\nexport function createMetadataPanel(blessed: typeof Blessed, options: MetadataPanelOptions) {\n const { parent, width, height, top = 0, right = 0 } = options;\n\n // Create box for metadata panel\n const panel = blessed.box({\n parent,\n top,\n right,\n width,\n height,\n tags: true,\n scrollable: true,\n alwaysScroll: true,\n style: {\n fg: Colors.fg.normal,\n bg: Colors.bg.main,\n },\n border: {\n type: \"line\",\n },\n label: \" Details \",\n });\n\n let currentEntry: ExplorerEntry | undefined;\n let _currentMetadata: EntryMetadata | undefined;\n\n return {\n element: panel,\n\n /**\n * Update panel with entry info\n */\n update(entry: ExplorerEntry, metadata?: EntryMetadata): void {\n currentEntry = entry;\n _currentMetadata = metadata;\n\n if (entry.type === \"up\") {\n panel.setContent(\" Parent directory\");\n (panel.screen as Blessed.Widgets.Screen)?.render();\n return;\n }\n\n const lines = formatMetadata(entry, metadata);\n panel.setContent(lines.map((l) => ` ${l}`).join(\"\\n\"));\n (panel.screen as Blessed.Widgets.Screen)?.render();\n },\n\n /**\n * Clear the panel\n */\n clear(): void {\n currentEntry = undefined;\n _currentMetadata = undefined;\n panel.setContent(\"\");\n (panel.screen as Blessed.Widgets.Screen)?.render();\n },\n\n /**\n * Get current entry\n */\n getCurrentEntry(): ExplorerEntry | undefined {\n return currentEntry;\n },\n\n /**\n * Destroy the component\n */\n destroy(): void {\n panel.destroy();\n },\n };\n}\n\nexport type MetadataPanel = ReturnType<typeof createMetadataPanel>;\n"],"mappings":";;;;;;AAqBA,SAAgB,eAAe,OAAsB,UAAoC;CACvF,MAAM,QAAkB,EAAE;CAG1B,MAAM,OAAO,MAAM,SAAS,SAAS,MAAM,MAAM,QAAQ;CACzD,MAAM,SAAS,OAAO,GAAG,KAAK,KAAK;AACnC,OAAM,KAAK,GAAG,SAAS,MAAM,OAAO;AACpC,OAAM,KAAK,GAAG;AAGd,OAAM,KAAK,SAAS,MAAM,OAAO;AAGjC,KAAI,MAAM,SAAS,UAAa,UAAU,SAAS,QAAW;EAC5D,MAAM,OAAO,UAAU,QAAQ,MAAM;AACrC,QAAM,KAAK,SAAS,WAAW,KAAM,GAAG;;AAI1C,KAAI,MAAM,kBAAkB,UAAa,UAAU,kBAAkB,QAAW;EAC9E,MAAM,QAAQ,UAAU,iBAAiB,MAAM;AAC/C,QAAM,KAAK,UAAU,QAAQ;;AAI/B,KAAI,MAAM,YAAY,UAAU,UAAU;EACxC,MAAM,OAAO,UAAU,YAAY,MAAM;AACzC,QAAM,KAAK,aAAa,eAAe,KAAM,GAAG;;AAIlD,KAAI,MAAM,YAAY,UAAU,SAC9B,OAAM,KAAK,aAAa,UAAU,YAAY,MAAM,WAAW;AAIjE,KAAI,MAAM,QAAQ,UAAU,MAAM;EAChC,MAAM,OAAO,UAAU,QAAQ,MAAM;EAErC,MAAM,UAAU,KAAM,SAAS,KAAK,GAAG,KAAM,MAAM,GAAG,GAAG,CAAC,OAAO;AACjE,QAAM,KAAK,SAAS,UAAU;;AAIhC,KAAI,UAAU,UACZ,OAAM,KAAK,UAAU,SAAS,YAAY;AAI5C,KAAI,UAAU,IACZ,OAAM,KAAK,QAAQ,SAAS,MAAM;AAIpC,KAAI,UAAU,SAAS,OAAO,KAAK,SAAS,MAAM,CAAC,SAAS,GAAG;EAE7D,MAAM,gBAAgB,IAAI,IAAI;GAC5B;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;AAEF,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,SAAS,MAAM,EAAE;AACzD,OAAI,cAAc,IAAI,IAAI,IAAI,UAAU,OAAW;GAGnD,MAAM,aAAa,IAAI,OAAO,EAAE,CAAC,aAAa,GAAG,IAAI,MAAM,EAAE;GAC7D,MAAM,eAAe,YAAY,MAAM;AAEvC,OAAI,aAAa,SAAS,KAAK,EAAE;AAC/B,UAAM,KAAK,GAAG;AACd,UAAM,KAAK,GAAG,WAAW,GAAG;AAC5B,SAAK,MAAM,QAAQ,aAAa,MAAM,KAAK,CACzC,OAAM,KAAK,KAAK,OAAO;SAGzB,OAAM,KAAK,GAAG,WAAW,IAAI,eAAe;;;AAKlD,QAAO;;;;;AAMT,SAAS,YAAY,OAAwB;AAC3C,KAAI,UAAU,QAAQ,UAAU,OAC9B,QAAO;AAET,KAAI,OAAO,UAAU,SACnB,QAAO;AAET,KAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAChD,QAAO,OAAO,MAAM;AAEtB,KAAI,MAAM,QAAQ,MAAM,EAAE;AACxB,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,MAAI,MAAM,OAAO,MAAM,OAAO,MAAM,YAAY,OAAO,MAAM,SAAS,CACpE,QAAO,MAAM,KAAK,KAAK;AAEzB,SAAO,KAAK,UAAU,OAAO,MAAM,EAAE;;AAEvC,KAAI,OAAO,UAAU,SACnB,QAAO,KAAK,UAAU,OAAO,MAAM,EAAE;AAEvC,QAAO,OAAO,MAAM;;;;;AAMtB,SAAS,eAAe,MAAoB;AAC1C,QAAO,KAAK,eAAe,MAAM;EAC/B,MAAM;EACN,OAAO;EACP,KAAK;EACL,MAAM;EACN,QAAQ;EACT,CAAC;;;;;AAMJ,SAAgB,oBAAoB,SAAyB,SAA+B;CAC1F,MAAM,EAAE,QAAQ,OAAO,QAAQ,MAAM,GAAG,QAAQ,MAAM;CAGtD,MAAM,QAAQ,QAAQ,IAAI;EACxB;EACA;EACA;EACA;EACA;EACA,MAAM;EACN,YAAY;EACZ,cAAc;EACd,OAAO;GACL,IAAI,OAAO,GAAG;GACd,IAAI,OAAO,GAAG;GACf;EACD,QAAQ,EACN,MAAM,QACP;EACD,OAAO;EACR,CAAC;CAEF,IAAI;AAGJ,QAAO;EACL,SAAS;EAKT,OAAO,OAAsB,UAAgC;AAC3D,kBAAe;AAGf,OAAI,MAAM,SAAS,MAAM;AACvB,UAAM,WAAW,oBAAoB;AACrC,IAAC,MAAM,QAAmC,QAAQ;AAClD;;GAGF,MAAM,QAAQ,eAAe,OAAO,SAAS;AAC7C,SAAM,WAAW,MAAM,KAAK,MAAM,IAAI,IAAI,CAAC,KAAK,KAAK,CAAC;AACtD,GAAC,MAAM,QAAmC,QAAQ;;EAMpD,QAAc;AACZ,kBAAe;AAEf,SAAM,WAAW,GAAG;AACpB,GAAC,MAAM,QAAmC,QAAQ;;EAMpD,kBAA6C;AAC3C,UAAO;;EAMT,UAAgB;AACd,SAAM,SAAS;;EAElB"}
@@ -115,9 +115,22 @@ async function createExplorerScreen(options) {
115
115
  async function handleExec() {
116
116
  const selected = require_actions.navigation.getSelected(store.getState());
117
117
  if (!selected || selected.type === "up") return;
118
- dialogs.showLoading("Executing action");
119
- const result = await require_actions.executeAction(runtime, selected.path, "default");
120
- dialogs.showActionResult("default", result.success, result.message, result.data);
118
+ if (selected.type !== "exec") {
119
+ dialogs.showError("Not Executable", `${selected.path} is not executable`);
120
+ return;
121
+ }
122
+ const inputSchema = (await require_actions.loadMetadata(runtime, selected))?.extra?.inputSchema;
123
+ const properties = inputSchema?.properties || {};
124
+ if (Object.keys(properties).length > 0) dialogs.showParamsInput(selected.path, inputSchema, async (params) => {
125
+ dialogs.showLoading("Executing action");
126
+ const result = await require_actions.executeAction(runtime, selected.path, "default", params);
127
+ dialogs.showActionResult("exec", result.success, result.message, result.data);
128
+ });
129
+ else {
130
+ dialogs.showLoading("Executing action");
131
+ const result = await require_actions.executeAction(runtime, selected.path, "default");
132
+ dialogs.showActionResult("exec", result.success, result.message, result.data);
133
+ }
121
134
  }
122
135
  async function handleRefresh() {
123
136
  await loadPath(store.getState().currentPath);
@@ -129,13 +142,15 @@ async function createExplorerScreen(options) {
129
142
  exec: handleExec,
130
143
  refresh: handleRefresh,
131
144
  quit: () => {
132
- dialogs.showConfirm("Are you sure you want to quit?", () => {
133
- screen.destroy();
134
- require_index.printLogo();
145
+ dialogs.showConfirm("Are you sure you want to quit?", async () => {
146
+ await runtime.close();
147
+ const logo = require_index.colors.brightCyan("▄▀█ █▀▀ █▀\n█▀█ █▀░ ▄█\n");
148
+ const tagline = require_index.colors.dim("Agentic File System");
135
149
  const versionPart = require_index.colors.green(`v${version}`);
136
150
  const mountPart = require_index.colors.yellow(`${mountCount} ${mountCount === 1 ? "mount" : "mounts"}`);
137
- console.log(`${versionPart} ${require_index.colors.dim("•")} ${mountPart}`);
138
- console.log(require_index.colors.dim("\nThanks for using AFS Explorer!\n"));
151
+ const farewell = `\n${logo}${tagline}\n\n${`${versionPart} ${require_index.colors.dim("•")} ${mountPart}`}\n${require_index.colors.dim("Thanks for using AFS Explorer!")}\n`;
152
+ screen.destroy();
153
+ process.stdout.write(farewell);
139
154
  process.exit(0);
140
155
  });
141
156
  },
@@ -1,4 +1,4 @@
1
- import { colors, printLogo } from "../ui/index.mjs";
1
+ import { colors } from "../ui/index.mjs";
2
2
  import { createInitialState, executeAction, getExplain, loadDirectory, loadMetadata, navigation, readFileContent } from "./actions.mjs";
3
3
  import { createDefaultRegistry } from "./keybindings.mjs";
4
4
  import { createDialogManager } from "./components/dialog.mjs";
@@ -113,9 +113,22 @@ async function createExplorerScreen(options) {
113
113
  async function handleExec() {
114
114
  const selected = navigation.getSelected(store.getState());
115
115
  if (!selected || selected.type === "up") return;
116
- dialogs.showLoading("Executing action");
117
- const result = await executeAction(runtime, selected.path, "default");
118
- dialogs.showActionResult("default", result.success, result.message, result.data);
116
+ if (selected.type !== "exec") {
117
+ dialogs.showError("Not Executable", `${selected.path} is not executable`);
118
+ return;
119
+ }
120
+ const inputSchema = (await loadMetadata(runtime, selected))?.extra?.inputSchema;
121
+ const properties = inputSchema?.properties || {};
122
+ if (Object.keys(properties).length > 0) dialogs.showParamsInput(selected.path, inputSchema, async (params) => {
123
+ dialogs.showLoading("Executing action");
124
+ const result = await executeAction(runtime, selected.path, "default", params);
125
+ dialogs.showActionResult("exec", result.success, result.message, result.data);
126
+ });
127
+ else {
128
+ dialogs.showLoading("Executing action");
129
+ const result = await executeAction(runtime, selected.path, "default");
130
+ dialogs.showActionResult("exec", result.success, result.message, result.data);
131
+ }
119
132
  }
120
133
  async function handleRefresh() {
121
134
  await loadPath(store.getState().currentPath);
@@ -127,13 +140,15 @@ async function createExplorerScreen(options) {
127
140
  exec: handleExec,
128
141
  refresh: handleRefresh,
129
142
  quit: () => {
130
- dialogs.showConfirm("Are you sure you want to quit?", () => {
131
- screen.destroy();
132
- printLogo();
143
+ dialogs.showConfirm("Are you sure you want to quit?", async () => {
144
+ await runtime.close();
145
+ const logo = colors.brightCyan("▄▀█ █▀▀ █▀\n█▀█ █▀░ ▄█\n");
146
+ const tagline = colors.dim("Agentic File System");
133
147
  const versionPart = colors.green(`v${version}`);
134
148
  const mountPart = colors.yellow(`${mountCount} ${mountCount === 1 ? "mount" : "mounts"}`);
135
- console.log(`${versionPart} ${colors.dim("•")} ${mountPart}`);
136
- console.log(colors.dim("\nThanks for using AFS Explorer!\n"));
149
+ const farewell = `\n${logo}${tagline}\n\n${`${versionPart} ${colors.dim("•")} ${mountPart}`}\n${colors.dim("Thanks for using AFS Explorer!")}\n`;
150
+ screen.destroy();
151
+ process.stdout.write(farewell);
137
152
  process.exit(0);
138
153
  });
139
154
  },
@@ -1 +1 @@
1
- {"version":3,"file":"screen.mjs","names":[],"sources":["../../src/explorer/screen.ts"],"sourcesContent":["/**\n * AFS Explorer Screen\n *\n * Main screen controller that manages all UI components and user interaction.\n */\n\nimport blessed from \"blessed\";\nimport type { AFSRuntime } from \"../runtime.js\";\nimport { colors, printLogo } from \"../ui/index.js\";\nimport {\n createInitialState,\n executeAction,\n getExplain,\n loadDirectory,\n loadMetadata,\n navigation,\n readFileContent,\n} from \"./actions.js\";\nimport {\n createDialogManager,\n createFileList,\n createFunctionBar,\n createMetadataPanel,\n createStatusBar,\n} from \"./components/index.js\";\nimport { createDefaultRegistry } from \"./keybindings.js\";\nimport { createStore } from \"./state.js\";\n\nexport interface ExplorerScreenOptions {\n runtime: AFSRuntime;\n startPath?: string;\n version: string;\n mountCount: number;\n}\n\n/**\n * Create and run the explorer screen\n */\nexport async function createExplorerScreen(options: ExplorerScreenOptions): Promise<void> {\n const { runtime, startPath = \"/\", version, mountCount } = options;\n\n // Create store with initial state\n const store = createStore(createInitialState(startPath));\n\n // Create key binding registry\n const registry = createDefaultRegistry();\n\n // Create blessed screen\n // Use 'xterm' terminal to avoid Setulc parsing warnings with xterm-256color\n const screen = blessed.screen({\n smartCSR: true,\n title: \"AFS Explorer\",\n terminal: \"xterm\",\n fullUnicode: true,\n warnings: false,\n });\n\n // Create components\n const _statusBar = createStatusBar(blessed, {\n parent: screen,\n store,\n width: \"100%\",\n top: 0,\n });\n\n const fileList = createFileList(blessed, {\n parent: screen,\n store,\n width: \"70%\",\n height: \"100%-2\",\n top: 1,\n left: 0,\n });\n\n const metadataPanel = createMetadataPanel(blessed, {\n parent: screen,\n width: \"30%\",\n height: \"100%-2\",\n top: 1,\n right: 0,\n });\n\n const _functionBar = createFunctionBar(blessed, {\n parent: screen,\n registry,\n width: \"100%\",\n bottom: 0,\n });\n\n const dialogs = createDialogManager(blessed, {\n parent: screen,\n });\n\n // Load initial directory\n async function loadPath(path: string): Promise<void> {\n store.setState({ loading: true, error: undefined });\n\n const result = await loadDirectory(runtime, path);\n\n store.setState({\n currentPath: path,\n entries: result.entries,\n selectedIndex: 0,\n scrollOffset: 0,\n loading: false,\n error: result.error,\n });\n\n // Update metadata panel for first entry\n updateMetadata();\n }\n\n // Update metadata panel for selected entry\n async function updateMetadata(): Promise<void> {\n const selected = navigation.getSelected(store.getState());\n if (selected) {\n const metadata = await loadMetadata(runtime, selected);\n metadataPanel.update(selected, metadata);\n } else {\n metadataPanel.clear();\n }\n }\n\n // Handle navigation\n async function handleEnter(): Promise<void> {\n const selected = navigation.getSelected(store.getState());\n if (!selected) return;\n\n if (selected.type === \"directory\" || selected.type === \"up\") {\n await loadPath(selected.path);\n } else if (selected.type === \"file\") {\n await handleView();\n }\n }\n\n // Handle back navigation\n async function handleBack(): Promise<void> {\n const state = store.getState();\n const parentPath = navigation.getParentPath(state.currentPath);\n if (parentPath !== state.currentPath) {\n await loadPath(parentPath);\n }\n }\n\n // Handle view action (F3)\n async function handleView(): Promise<void> {\n const selected = navigation.getSelected(store.getState());\n if (!selected || selected.type === \"up\") return;\n\n if (selected.type === \"file\") {\n dialogs.showLoading(\"Loading file\");\n const result = await readFileContent(runtime, selected.path);\n if (result.error) {\n dialogs.showError(\"View Error\", result.error);\n } else {\n dialogs.showFileView(selected.path, result.content);\n }\n } else if (selected.type === \"directory\") {\n // Enter directory\n await loadPath(selected.path);\n }\n }\n\n // Handle explain action (F2)\n async function handleExplain(): Promise<void> {\n const selected = navigation.getSelected(store.getState());\n if (!selected || selected.type === \"up\") return;\n\n dialogs.showLoading(\"Getting explain info\");\n const result = await getExplain(runtime, selected.path);\n if (result.error) {\n dialogs.showError(\"Explain Error\", result.error);\n } else {\n dialogs.showExplain(selected.path, result.content);\n }\n }\n\n // Handle exec action (F4)\n async function handleExec(): Promise<void> {\n const selected = navigation.getSelected(store.getState());\n if (!selected || selected.type === \"up\") return;\n\n dialogs.showLoading(\"Executing action\");\n const result = await executeAction(runtime, selected.path, \"default\");\n dialogs.showActionResult(\"default\", result.success, result.message, result.data);\n }\n\n // Handle refresh action (F5)\n async function handleRefresh(): Promise<void> {\n const state = store.getState();\n await loadPath(state.currentPath);\n }\n\n // Action handlers map\n const actionHandlers: Record<string, () => void | Promise<void>> = {\n help: () => dialogs.showHelp(registry),\n explain: handleExplain,\n view: handleView,\n exec: handleExec,\n refresh: handleRefresh,\n quit: () => {\n dialogs.showConfirm(\"Are you sure you want to quit?\", () => {\n screen.destroy();\n // Show farewell message with version and mount info\n printLogo();\n const versionPart = colors.green(`v${version}`);\n const mountPart = colors.yellow(`${mountCount} ${mountCount === 1 ? \"mount\" : \"mounts\"}`);\n console.log(`${versionPart} ${colors.dim(\"•\")} ${mountPart}`);\n console.log(colors.dim(\"\\nThanks for using AFS Explorer!\\n\"));\n process.exit(0);\n });\n },\n \"nav:up\": () => {\n store.setState(navigation.up(store.getState()));\n updateMetadata();\n },\n \"nav:down\": () => {\n store.setState(navigation.down(store.getState()));\n updateMetadata();\n },\n \"nav:enter\": handleEnter,\n \"nav:back\": handleBack,\n \"nav:home\": () => {\n store.setState(navigation.home(store.getState()));\n updateMetadata();\n },\n \"nav:end\": () => {\n store.setState(navigation.end(store.getState()));\n updateMetadata();\n },\n \"nav:pageup\": () => {\n store.setState(navigation.pageUp(store.getState(), fileList.getVisibleHeight()));\n updateMetadata();\n },\n \"nav:pagedown\": () => {\n store.setState(navigation.pageDown(store.getState(), fileList.getVisibleHeight()));\n updateMetadata();\n },\n cancel: () => {\n if (dialogs.isOpen()) {\n dialogs.close();\n } else {\n // ESC on main screen triggers quit with confirmation\n actionHandlers.quit?.();\n }\n },\n };\n\n // Bind keys\n screen.on(\"keypress\", async (ch, key) => {\n // Skip if dialog is handling keys\n if (dialogs.isOpen()) return;\n\n const keyName = key.name || ch;\n if (!keyName) return;\n\n // Note: Not passing full context since default bindings don't have conditions\n const binding = registry.getBindingForKey(keyName);\n\n if (binding) {\n const handler = actionHandlers[binding.action];\n if (handler) {\n await handler();\n screen.render();\n }\n }\n });\n\n // Handle resize\n screen.on(\"resize\", () => {\n screen.render();\n });\n\n // Focus file list\n fileList.focus();\n\n // Load initial directory\n await loadPath(startPath);\n\n // Render\n screen.render();\n\n // Return promise that resolves when screen is destroyed\n return new Promise((resolve) => {\n screen.on(\"destroy\", () => {\n resolve();\n });\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"screen.mjs","names":[],"sources":["../../src/explorer/screen.ts"],"sourcesContent":["/**\n * AFS Explorer Screen\n *\n * Main screen controller that manages all UI components and user interaction.\n */\n\nimport blessed from \"blessed\";\nimport type { AFSRuntime } from \"../runtime.js\";\nimport { colors } from \"../ui/index.js\";\nimport {\n createInitialState,\n executeAction,\n getExplain,\n loadDirectory,\n loadMetadata,\n navigation,\n readFileContent,\n} from \"./actions.js\";\nimport {\n createDialogManager,\n createFileList,\n createFunctionBar,\n createMetadataPanel,\n createStatusBar,\n} from \"./components/index.js\";\nimport { createDefaultRegistry } from \"./keybindings.js\";\nimport { createStore } from \"./state.js\";\n\nexport interface ExplorerScreenOptions {\n runtime: AFSRuntime;\n startPath?: string;\n version: string;\n mountCount: number;\n}\n\n/**\n * Create and run the explorer screen\n */\nexport async function createExplorerScreen(options: ExplorerScreenOptions): Promise<void> {\n const { runtime, startPath = \"/\", version, mountCount } = options;\n\n // Create store with initial state\n const store = createStore(createInitialState(startPath));\n\n // Create key binding registry\n const registry = createDefaultRegistry();\n\n // Create blessed screen\n // Use 'xterm' terminal to avoid Setulc parsing warnings with xterm-256color\n const screen = blessed.screen({\n smartCSR: true,\n title: \"AFS Explorer\",\n terminal: \"xterm\",\n fullUnicode: true,\n warnings: false,\n });\n\n // Create components\n const _statusBar = createStatusBar(blessed, {\n parent: screen,\n store,\n width: \"100%\",\n top: 0,\n });\n\n const fileList = createFileList(blessed, {\n parent: screen,\n store,\n width: \"70%\",\n height: \"100%-2\",\n top: 1,\n left: 0,\n });\n\n const metadataPanel = createMetadataPanel(blessed, {\n parent: screen,\n width: \"30%\",\n height: \"100%-2\",\n top: 1,\n right: 0,\n });\n\n const _functionBar = createFunctionBar(blessed, {\n parent: screen,\n registry,\n width: \"100%\",\n bottom: 0,\n });\n\n const dialogs = createDialogManager(blessed, {\n parent: screen,\n });\n\n // Load initial directory\n async function loadPath(path: string): Promise<void> {\n store.setState({ loading: true, error: undefined });\n\n const result = await loadDirectory(runtime, path);\n\n store.setState({\n currentPath: path,\n entries: result.entries,\n selectedIndex: 0,\n scrollOffset: 0,\n loading: false,\n error: result.error,\n });\n\n // Update metadata panel for first entry\n updateMetadata();\n }\n\n // Update metadata panel for selected entry\n async function updateMetadata(): Promise<void> {\n const selected = navigation.getSelected(store.getState());\n if (selected) {\n const metadata = await loadMetadata(runtime, selected);\n metadataPanel.update(selected, metadata);\n } else {\n metadataPanel.clear();\n }\n }\n\n // Handle navigation\n async function handleEnter(): Promise<void> {\n const selected = navigation.getSelected(store.getState());\n if (!selected) return;\n\n if (selected.type === \"directory\" || selected.type === \"up\") {\n await loadPath(selected.path);\n } else if (selected.type === \"file\") {\n await handleView();\n }\n }\n\n // Handle back navigation\n async function handleBack(): Promise<void> {\n const state = store.getState();\n const parentPath = navigation.getParentPath(state.currentPath);\n if (parentPath !== state.currentPath) {\n await loadPath(parentPath);\n }\n }\n\n // Handle view action (F3)\n async function handleView(): Promise<void> {\n const selected = navigation.getSelected(store.getState());\n if (!selected || selected.type === \"up\") return;\n\n if (selected.type === \"file\") {\n dialogs.showLoading(\"Loading file\");\n const result = await readFileContent(runtime, selected.path);\n if (result.error) {\n dialogs.showError(\"View Error\", result.error);\n } else {\n dialogs.showFileView(selected.path, result.content);\n }\n } else if (selected.type === \"directory\") {\n // Enter directory\n await loadPath(selected.path);\n }\n }\n\n // Handle explain action (F2)\n async function handleExplain(): Promise<void> {\n const selected = navigation.getSelected(store.getState());\n if (!selected || selected.type === \"up\") return;\n\n dialogs.showLoading(\"Getting explain info\");\n const result = await getExplain(runtime, selected.path);\n if (result.error) {\n dialogs.showError(\"Explain Error\", result.error);\n } else {\n dialogs.showExplain(selected.path, result.content);\n }\n }\n\n // Handle exec action (F4)\n async function handleExec(): Promise<void> {\n const selected = navigation.getSelected(store.getState());\n if (!selected || selected.type === \"up\") return;\n\n // Check if entry is executable\n if (selected.type !== \"exec\") {\n dialogs.showError(\"Not Executable\", `${selected.path} is not executable`);\n return;\n }\n\n // Get metadata to check for inputSchema\n const metadata = await loadMetadata(runtime, selected);\n const inputSchema = metadata?.extra?.inputSchema as Record<string, unknown> | undefined;\n\n // Check if we need parameters\n const properties = (inputSchema?.properties as Record<string, unknown>) || {};\n const hasParams = Object.keys(properties).length > 0;\n\n if (hasParams) {\n // Show params input dialog\n dialogs.showParamsInput(selected.path, inputSchema, async (params) => {\n dialogs.showLoading(\"Executing action\");\n const result = await executeAction(runtime, selected.path, \"default\", params);\n dialogs.showActionResult(\"exec\", result.success, result.message, result.data);\n });\n } else {\n // Execute directly without params\n dialogs.showLoading(\"Executing action\");\n const result = await executeAction(runtime, selected.path, \"default\");\n dialogs.showActionResult(\"exec\", result.success, result.message, result.data);\n }\n }\n\n // Handle refresh action (F5)\n async function handleRefresh(): Promise<void> {\n const state = store.getState();\n await loadPath(state.currentPath);\n }\n\n // Action handlers map\n const actionHandlers: Record<string, () => void | Promise<void>> = {\n help: () => dialogs.showHelp(registry),\n explain: handleExplain,\n view: handleView,\n exec: handleExec,\n refresh: handleRefresh,\n quit: () => {\n dialogs.showConfirm(\"Are you sure you want to quit?\", async () => {\n // Properly close the runtime to disconnect all providers\n await runtime.close();\n // Prepare farewell message before destroying screen (match header.ts format)\n const logo = colors.brightCyan(\"▄▀█ █▀▀ █▀\\n█▀█ █▀░ ▄█\\n\");\n const tagline = colors.dim(\"Agentic File System\");\n const versionPart = colors.green(`v${version}`);\n const mountPart = colors.yellow(`${mountCount} ${mountCount === 1 ? \"mount\" : \"mounts\"}`);\n const statusLine = `${versionPart} ${colors.dim(\"•\")} ${mountPart}`;\n const farewell = `\\n${logo}${tagline}\\n\\n${statusLine}\\n${colors.dim(\"Thanks for using AFS Explorer!\")}\\n`;\n // Destroy screen and immediately print\n screen.destroy();\n process.stdout.write(farewell);\n process.exit(0);\n });\n },\n \"nav:up\": () => {\n store.setState(navigation.up(store.getState()));\n updateMetadata();\n },\n \"nav:down\": () => {\n store.setState(navigation.down(store.getState()));\n updateMetadata();\n },\n \"nav:enter\": handleEnter,\n \"nav:back\": handleBack,\n \"nav:home\": () => {\n store.setState(navigation.home(store.getState()));\n updateMetadata();\n },\n \"nav:end\": () => {\n store.setState(navigation.end(store.getState()));\n updateMetadata();\n },\n \"nav:pageup\": () => {\n store.setState(navigation.pageUp(store.getState(), fileList.getVisibleHeight()));\n updateMetadata();\n },\n \"nav:pagedown\": () => {\n store.setState(navigation.pageDown(store.getState(), fileList.getVisibleHeight()));\n updateMetadata();\n },\n cancel: () => {\n if (dialogs.isOpen()) {\n dialogs.close();\n } else {\n // ESC on main screen triggers quit with confirmation\n actionHandlers.quit?.();\n }\n },\n };\n\n // Bind keys\n screen.on(\"keypress\", async (ch, key) => {\n // Skip if dialog is handling keys\n if (dialogs.isOpen()) return;\n\n const keyName = key.name || ch;\n if (!keyName) return;\n\n // Note: Not passing full context since default bindings don't have conditions\n const binding = registry.getBindingForKey(keyName);\n\n if (binding) {\n const handler = actionHandlers[binding.action];\n if (handler) {\n await handler();\n screen.render();\n }\n }\n });\n\n // Handle resize\n screen.on(\"resize\", () => {\n screen.render();\n });\n\n // Focus file list\n fileList.focus();\n\n // Load initial directory\n await loadPath(startPath);\n\n // Render\n screen.render();\n\n // Return promise that resolves when screen is destroyed\n return new Promise((resolve) => {\n screen.on(\"destroy\", () => {\n resolve();\n });\n });\n}\n"],"mappings":"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"}
@@ -14,7 +14,9 @@ const Colors = {
14
14
  selected: "cyan",
15
15
  functionKey: "cyan",
16
16
  dialog: "blue",
17
- dialogBorder: "white"
17
+ dialogBorder: "white",
18
+ input: "black",
19
+ inputFocus: "cyan"
18
20
  },
19
21
  fg: {
20
22
  normal: "cyan",
@@ -13,7 +13,9 @@ const Colors = {
13
13
  selected: "cyan",
14
14
  functionKey: "cyan",
15
15
  dialog: "blue",
16
- dialogBorder: "white"
16
+ dialogBorder: "white",
17
+ input: "black",
18
+ inputFocus: "cyan"
17
19
  },
18
20
  fg: {
19
21
  normal: "cyan",
@@ -1 +1 @@
1
- {"version":3,"file":"theme.mjs","names":[],"sources":["../../src/explorer/theme.ts"],"sourcesContent":["/**\n * AFS Explorer Theme\n *\n * PC Tools Deluxe classic blue color scheme\n */\n\n/**\n * Color definitions for PC Tools theme\n */\nexport const Colors = {\n // Background colors\n bg: {\n main: \"blue\",\n selected: \"cyan\",\n functionKey: \"cyan\",\n dialog: \"blue\",\n dialogBorder: \"white\",\n },\n\n // Foreground colors (using standard blessed color names)\n fg: {\n normal: \"cyan\",\n selected: \"black\",\n title: \"white\",\n border: \"cyan\",\n directory: \"yellow\",\n file: \"white\",\n exec: \"green\",\n link: \"magenta\",\n up: \"yellow\",\n size: \"cyan\",\n date: \"cyan\",\n functionKeyLabel: \"black\",\n functionKeyText: \"white\",\n status: \"yellow\",\n error: \"red\",\n muted: \"gray\",\n },\n} as const;\n\n/**\n * Blessed style objects for components\n */\nexport const Styles = {\n /** Main screen background */\n screen: {\n bg: Colors.bg.main,\n },\n\n /** Box/panel borders */\n border: {\n type: \"line\" as const,\n fg: Colors.fg.border,\n bg: Colors.bg.main,\n },\n\n /** Title bar style */\n title: {\n fg: Colors.fg.title,\n bg: Colors.bg.main,\n bold: true,\n },\n\n /** Normal list item */\n listItem: {\n fg: Colors.fg.normal,\n bg: Colors.bg.main,\n },\n\n /** Selected list item */\n listItemSelected: {\n fg: Colors.fg.selected,\n bg: Colors.bg.selected,\n bold: true,\n },\n\n /** Directory entry */\n directory: {\n fg: Colors.fg.directory,\n bg: Colors.bg.main,\n bold: true,\n },\n\n /** Directory entry selected */\n directorySelected: {\n fg: \"black\",\n bg: Colors.bg.selected,\n bold: true,\n },\n\n /** File entry */\n file: {\n fg: Colors.fg.file,\n bg: Colors.bg.main,\n },\n\n /** Exec entry */\n exec: {\n fg: Colors.fg.exec,\n bg: Colors.bg.main,\n },\n\n /** Function key label (e.g., \"F1\") */\n functionKeyLabel: {\n fg: Colors.fg.functionKeyLabel,\n bg: Colors.bg.functionKey,\n },\n\n /** Function key text (e.g., \"Help\") */\n functionKeyText: {\n fg: Colors.fg.functionKeyText,\n bg: Colors.bg.main,\n },\n\n /** Status bar */\n status: {\n fg: Colors.fg.status,\n bg: Colors.bg.main,\n },\n\n /** Metadata panel */\n metadata: {\n fg: Colors.fg.normal,\n bg: Colors.bg.main,\n },\n\n /** Metadata label */\n metadataLabel: {\n fg: Colors.fg.muted,\n bg: Colors.bg.main,\n },\n\n /** Metadata value */\n metadataValue: {\n fg: Colors.fg.normal,\n bg: Colors.bg.main,\n },\n\n /** Error message */\n error: {\n fg: Colors.fg.error,\n bg: Colors.bg.main,\n bold: true,\n },\n\n /** Dialog box */\n dialog: {\n fg: Colors.fg.normal,\n bg: Colors.bg.dialog,\n border: {\n type: \"line\" as const,\n fg: Colors.bg.dialogBorder,\n bg: Colors.bg.dialog,\n },\n },\n} as const;\n\n/**\n * Icons for different entry types\n * Using ASCII characters for better terminal compatibility\n */\nexport const Icons = {\n up: \"[D]\",\n directory: \"[D]\",\n file: \" \",\n exec: \"[X]\",\n link: \"[L]\",\n selected: \">\",\n unselected: \" \",\n} as const;\n\n/**\n * UI symbols\n * Using ASCII characters for better terminal compatibility\n */\nexport const Symbols = {\n folder: \"[D]\",\n error: \"X\",\n success: \"OK\",\n scrollbar: \"#\",\n loading: \"...\",\n} as const;\n\n/**\n * Box drawing characters\n */\nexport const BoxChars = {\n topLeft: \"┌\",\n topRight: \"┐\",\n bottomLeft: \"└\",\n bottomRight: \"┘\",\n horizontal: \"─\",\n vertical: \"│\",\n leftT: \"├\",\n rightT: \"┤\",\n topT: \"┬\",\n bottomT: \"┴\",\n cross: \"┼\",\n} as const;\n\n/**\n * Get style for entry based on type and selection state\n */\nexport function getEntryStyle(\n type: \"file\" | \"directory\" | \"exec\" | \"link\" | \"up\",\n selected: boolean,\n): { fg: string; bg: string; bold?: boolean } {\n if (selected) {\n if (type === \"directory\" || type === \"up\") {\n return Styles.directorySelected;\n }\n return Styles.listItemSelected;\n }\n\n switch (type) {\n case \"directory\":\n case \"up\":\n return Styles.directory;\n case \"exec\":\n return Styles.exec;\n default:\n return Styles.file;\n }\n}\n\n/**\n * Get icon for entry type\n */\nexport function getEntryIcon(type: \"file\" | \"directory\" | \"exec\" | \"link\" | \"up\"): string {\n return Icons[type] || Icons.file;\n}\n\n/**\n * Format file size for display\n */\nexport function formatSize(bytes: number | undefined): string {\n if (bytes === undefined) return \"\";\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)}GB`;\n}\n\n/**\n * Format date for display\n */\nexport function formatDate(date: Date | undefined): string {\n if (!date) return \"\";\n const now = new Date();\n const diff = now.getTime() - date.getTime();\n const days = Math.floor(diff / (1000 * 60 * 60 * 24));\n\n if (days === 0) {\n return date.toLocaleTimeString([], { hour: \"2-digit\", minute: \"2-digit\" });\n }\n if (days < 7) {\n return `${days}d ago`;\n }\n return date.toLocaleDateString([], { month: \"short\", day: \"numeric\" });\n}\n\n/**\n * Format an entry line for display (not used in blessed components)\n */\nexport function formatEntryLine(\n name: string,\n type: \"file\" | \"directory\" | \"exec\" | \"link\" | \"up\",\n size?: number,\n date?: Date,\n): string {\n const icon = getEntryIcon(type);\n const sizeStr = formatSize(size);\n const dateStr = formatDate(date);\n return `${icon} ${name} ${sizeStr} ${dateStr}`;\n}\n"],"mappings":";;;;;;;;;AASA,MAAa,SAAS;CAEpB,IAAI;EACF,MAAM;EACN,UAAU;EACV,aAAa;EACb,QAAQ;EACR,cAAc;EACf;CAGD,IAAI;EACF,QAAQ;EACR,UAAU;EACV,OAAO;EACP,QAAQ;EACR,WAAW;EACX,MAAM;EACN,MAAM;EACN,MAAM;EACN,IAAI;EACJ,MAAM;EACN,MAAM;EACN,kBAAkB;EAClB,iBAAiB;EACjB,QAAQ;EACR,OAAO;EACP,OAAO;EACR;CACF;;;;AAKD,MAAa,SAAS;CAEpB,QAAQ,EACN,IAAI,OAAO,GAAG,MACf;CAGD,QAAQ;EACN,MAAM;EACN,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,OAAO;EACL,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACd,MAAM;EACP;CAGD,UAAU;EACR,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,kBAAkB;EAChB,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACd,MAAM;EACP;CAGD,WAAW;EACT,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACd,MAAM;EACP;CAGD,mBAAmB;EACjB,IAAI;EACJ,IAAI,OAAO,GAAG;EACd,MAAM;EACP;CAGD,MAAM;EACJ,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,MAAM;EACJ,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,kBAAkB;EAChB,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,iBAAiB;EACf,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,QAAQ;EACN,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,UAAU;EACR,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,eAAe;EACb,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,eAAe;EACb,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,OAAO;EACL,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACd,MAAM;EACP;CAGD,QAAQ;EACN,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACd,QAAQ;GACN,MAAM;GACN,IAAI,OAAO,GAAG;GACd,IAAI,OAAO,GAAG;GACf;EACF;CACF;;;;;AAMD,MAAa,QAAQ;CACnB,IAAI;CACJ,WAAW;CACX,MAAM;CACN,MAAM;CACN,MAAM;CACN,UAAU;CACV,YAAY;CACb;;;;;AAMD,MAAa,UAAU;CACrB,QAAQ;CACR,OAAO;CACP,SAAS;CACT,WAAW;CACX,SAAS;CACV;;;;AAsDD,SAAgB,WAAW,OAAmC;AAC5D,KAAI,UAAU,OAAW,QAAO;AAChC,KAAI,QAAQ,KAAM,QAAO,GAAG,MAAM;AAClC,KAAI,QAAQ,OAAO,KAAM,QAAO,IAAI,QAAQ,MAAM,QAAQ,EAAE,CAAC;AAC7D,KAAI,QAAQ,OAAO,OAAO,KAAM,QAAO,IAAI,SAAS,OAAO,OAAO,QAAQ,EAAE,CAAC;AAC7E,QAAO,IAAI,SAAS,OAAO,OAAO,OAAO,QAAQ,EAAE,CAAC"}
1
+ {"version":3,"file":"theme.mjs","names":[],"sources":["../../src/explorer/theme.ts"],"sourcesContent":["/**\n * AFS Explorer Theme\n *\n * PC Tools Deluxe classic blue color scheme\n */\n\n/**\n * Color definitions for PC Tools theme\n */\nexport const Colors = {\n // Background colors\n bg: {\n main: \"blue\",\n selected: \"cyan\",\n functionKey: \"cyan\",\n dialog: \"blue\",\n dialogBorder: \"white\",\n input: \"black\",\n inputFocus: \"cyan\",\n },\n\n // Foreground colors (using standard blessed color names)\n fg: {\n normal: \"cyan\",\n selected: \"black\",\n title: \"white\",\n border: \"cyan\",\n directory: \"yellow\",\n file: \"white\",\n exec: \"green\",\n link: \"magenta\",\n up: \"yellow\",\n size: \"cyan\",\n date: \"cyan\",\n functionKeyLabel: \"black\",\n functionKeyText: \"white\",\n status: \"yellow\",\n error: \"red\",\n muted: \"gray\",\n },\n} as const;\n\n/**\n * Blessed style objects for components\n */\nexport const Styles = {\n /** Main screen background */\n screen: {\n bg: Colors.bg.main,\n },\n\n /** Box/panel borders */\n border: {\n type: \"line\" as const,\n fg: Colors.fg.border,\n bg: Colors.bg.main,\n },\n\n /** Title bar style */\n title: {\n fg: Colors.fg.title,\n bg: Colors.bg.main,\n bold: true,\n },\n\n /** Normal list item */\n listItem: {\n fg: Colors.fg.normal,\n bg: Colors.bg.main,\n },\n\n /** Selected list item */\n listItemSelected: {\n fg: Colors.fg.selected,\n bg: Colors.bg.selected,\n bold: true,\n },\n\n /** Directory entry */\n directory: {\n fg: Colors.fg.directory,\n bg: Colors.bg.main,\n bold: true,\n },\n\n /** Directory entry selected */\n directorySelected: {\n fg: \"black\",\n bg: Colors.bg.selected,\n bold: true,\n },\n\n /** File entry */\n file: {\n fg: Colors.fg.file,\n bg: Colors.bg.main,\n },\n\n /** Exec entry */\n exec: {\n fg: Colors.fg.exec,\n bg: Colors.bg.main,\n },\n\n /** Function key label (e.g., \"F1\") */\n functionKeyLabel: {\n fg: Colors.fg.functionKeyLabel,\n bg: Colors.bg.functionKey,\n },\n\n /** Function key text (e.g., \"Help\") */\n functionKeyText: {\n fg: Colors.fg.functionKeyText,\n bg: Colors.bg.main,\n },\n\n /** Status bar */\n status: {\n fg: Colors.fg.status,\n bg: Colors.bg.main,\n },\n\n /** Metadata panel */\n metadata: {\n fg: Colors.fg.normal,\n bg: Colors.bg.main,\n },\n\n /** Metadata label */\n metadataLabel: {\n fg: Colors.fg.muted,\n bg: Colors.bg.main,\n },\n\n /** Metadata value */\n metadataValue: {\n fg: Colors.fg.normal,\n bg: Colors.bg.main,\n },\n\n /** Error message */\n error: {\n fg: Colors.fg.error,\n bg: Colors.bg.main,\n bold: true,\n },\n\n /** Dialog box */\n dialog: {\n fg: Colors.fg.normal,\n bg: Colors.bg.dialog,\n border: {\n type: \"line\" as const,\n fg: Colors.bg.dialogBorder,\n bg: Colors.bg.dialog,\n },\n },\n} as const;\n\n/**\n * Icons for different entry types\n * Using ASCII characters for better terminal compatibility\n */\nexport const Icons = {\n up: \"[D]\",\n directory: \"[D]\",\n file: \" \",\n exec: \"[X]\",\n link: \"[L]\",\n selected: \">\",\n unselected: \" \",\n} as const;\n\n/**\n * UI symbols\n * Using ASCII characters for better terminal compatibility\n */\nexport const Symbols = {\n folder: \"[D]\",\n error: \"X\",\n success: \"OK\",\n scrollbar: \"#\",\n loading: \"...\",\n} as const;\n\n/**\n * Box drawing characters\n */\nexport const BoxChars = {\n topLeft: \"┌\",\n topRight: \"┐\",\n bottomLeft: \"└\",\n bottomRight: \"┘\",\n horizontal: \"─\",\n vertical: \"│\",\n leftT: \"├\",\n rightT: \"┤\",\n topT: \"┬\",\n bottomT: \"┴\",\n cross: \"┼\",\n} as const;\n\n/**\n * Get style for entry based on type and selection state\n */\nexport function getEntryStyle(\n type: \"file\" | \"directory\" | \"exec\" | \"link\" | \"up\",\n selected: boolean,\n): { fg: string; bg: string; bold?: boolean } {\n if (selected) {\n if (type === \"directory\" || type === \"up\") {\n return Styles.directorySelected;\n }\n return Styles.listItemSelected;\n }\n\n switch (type) {\n case \"directory\":\n case \"up\":\n return Styles.directory;\n case \"exec\":\n return Styles.exec;\n default:\n return Styles.file;\n }\n}\n\n/**\n * Get icon for entry type\n */\nexport function getEntryIcon(type: \"file\" | \"directory\" | \"exec\" | \"link\" | \"up\"): string {\n return Icons[type] || Icons.file;\n}\n\n/**\n * Format file size for display\n */\nexport function formatSize(bytes: number | undefined): string {\n if (bytes === undefined) return \"\";\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)}GB`;\n}\n\n/**\n * Format date for display\n */\nexport function formatDate(date: Date | undefined): string {\n if (!date) return \"\";\n const now = new Date();\n const diff = now.getTime() - date.getTime();\n const days = Math.floor(diff / (1000 * 60 * 60 * 24));\n\n if (days === 0) {\n return date.toLocaleTimeString([], { hour: \"2-digit\", minute: \"2-digit\" });\n }\n if (days < 7) {\n return `${days}d ago`;\n }\n return date.toLocaleDateString([], { month: \"short\", day: \"numeric\" });\n}\n\n/**\n * Format an entry line for display (not used in blessed components)\n */\nexport function formatEntryLine(\n name: string,\n type: \"file\" | \"directory\" | \"exec\" | \"link\" | \"up\",\n size?: number,\n date?: Date,\n): string {\n const icon = getEntryIcon(type);\n const sizeStr = formatSize(size);\n const dateStr = formatDate(date);\n return `${icon} ${name} ${sizeStr} ${dateStr}`;\n}\n"],"mappings":";;;;;;;;;AASA,MAAa,SAAS;CAEpB,IAAI;EACF,MAAM;EACN,UAAU;EACV,aAAa;EACb,QAAQ;EACR,cAAc;EACd,OAAO;EACP,YAAY;EACb;CAGD,IAAI;EACF,QAAQ;EACR,UAAU;EACV,OAAO;EACP,QAAQ;EACR,WAAW;EACX,MAAM;EACN,MAAM;EACN,MAAM;EACN,IAAI;EACJ,MAAM;EACN,MAAM;EACN,kBAAkB;EAClB,iBAAiB;EACjB,QAAQ;EACR,OAAO;EACP,OAAO;EACR;CACF;;;;AAKD,MAAa,SAAS;CAEpB,QAAQ,EACN,IAAI,OAAO,GAAG,MACf;CAGD,QAAQ;EACN,MAAM;EACN,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,OAAO;EACL,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACd,MAAM;EACP;CAGD,UAAU;EACR,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,kBAAkB;EAChB,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACd,MAAM;EACP;CAGD,WAAW;EACT,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACd,MAAM;EACP;CAGD,mBAAmB;EACjB,IAAI;EACJ,IAAI,OAAO,GAAG;EACd,MAAM;EACP;CAGD,MAAM;EACJ,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,MAAM;EACJ,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,kBAAkB;EAChB,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,iBAAiB;EACf,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,QAAQ;EACN,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,UAAU;EACR,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,eAAe;EACb,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,eAAe;EACb,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACf;CAGD,OAAO;EACL,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACd,MAAM;EACP;CAGD,QAAQ;EACN,IAAI,OAAO,GAAG;EACd,IAAI,OAAO,GAAG;EACd,QAAQ;GACN,MAAM;GACN,IAAI,OAAO,GAAG;GACd,IAAI,OAAO,GAAG;GACf;EACF;CACF;;;;;AAMD,MAAa,QAAQ;CACnB,IAAI;CACJ,WAAW;CACX,MAAM;CACN,MAAM;CACN,MAAM;CACN,UAAU;CACV,YAAY;CACb;;;;;AAMD,MAAa,UAAU;CACrB,QAAQ;CACR,OAAO;CACP,SAAS;CACT,WAAW;CACX,SAAS;CACV;;;;AAsDD,SAAgB,WAAW,OAAmC;AAC5D,KAAI,UAAU,OAAW,QAAO;AAChC,KAAI,QAAQ,KAAM,QAAO,GAAG,MAAM;AAClC,KAAI,QAAQ,OAAO,KAAM,QAAO,IAAI,QAAQ,MAAM,QAAQ,EAAE,CAAC;AAC7D,KAAI,QAAQ,OAAO,OAAO,KAAM,QAAO,IAAI,SAAS,OAAO,OAAO,QAAQ,EAAE,CAAC;AAC7E,QAAO,IAAI,SAAS,OAAO,OAAO,OAAO,QAAQ,EAAE,CAAC"}
@@ -101,4 +101,5 @@ function cliPathToCanonical(input) {
101
101
  }
102
102
 
103
103
  //#endregion
104
- exports.cliPathToCanonical = cliPathToCanonical;
104
+ exports.cliPathToCanonical = cliPathToCanonical;
105
+ exports.parseCliPath = parseCliPath;
@@ -100,5 +100,5 @@ function cliPathToCanonical(input) {
100
100
  }
101
101
 
102
102
  //#endregion
103
- export { cliPathToCanonical };
103
+ export { cliPathToCanonical, parseCliPath };
104
104
  //# sourceMappingURL=path-utils.mjs.map
package/dist/runtime.cjs CHANGED
@@ -15,6 +15,7 @@ let _aigne_afs = require("@aigne/afs");
15
15
  */
16
16
  var AFSRuntime = class {
17
17
  afs;
18
+ mountedModules = [];
18
19
  constructor() {
19
20
  this.afs = new _aigne_afs.AFS();
20
21
  }
@@ -28,6 +29,21 @@ var AFSRuntime = class {
28
29
  async mount(provider, mountPath, options) {
29
30
  const namespace = options?.namespace ?? null;
30
31
  this.afs.mount(provider, mountPath, { namespace });
32
+ this.mountedModules.push(provider);
33
+ }
34
+ /**
35
+ * Close the runtime and disconnect all mounted providers.
36
+ * This should be called before process exit to ensure proper cleanup.
37
+ */
38
+ async close() {
39
+ const disconnectPromises = this.mountedModules.map(async (module) => {
40
+ try {
41
+ if ("onUnmount" in module && typeof module.onUnmount === "function") await module.onUnmount();
42
+ if ("disconnect" in module && typeof module.disconnect === "function") await module.disconnect();
43
+ } catch {}
44
+ });
45
+ await Promise.all(disconnectPromises);
46
+ this.mountedModules = [];
31
47
  }
32
48
  /**
33
49
  * Get all mounts, optionally filtered by namespace
@@ -74,6 +90,14 @@ var AFSRuntime = class {
74
90
  const canonicalPath = this.translatePath(path);
75
91
  return this.afs.delete(canonicalPath, options);
76
92
  }
93
+ async exec(path, args = {}, options) {
94
+ const canonicalPath = this.translatePath(path);
95
+ return this.afs.exec(canonicalPath, args, options ?? {});
96
+ }
97
+ async stat(path, options) {
98
+ const canonicalPath = this.translatePath(path);
99
+ return this.afs.stat(canonicalPath, options);
100
+ }
77
101
  };
78
102
  /**
79
103
  * Create an AFS runtime from configuration
package/dist/runtime.mjs CHANGED
@@ -14,6 +14,7 @@ import { AFS } from "@aigne/afs";
14
14
  */
15
15
  var AFSRuntime = class {
16
16
  afs;
17
+ mountedModules = [];
17
18
  constructor() {
18
19
  this.afs = new AFS();
19
20
  }
@@ -27,6 +28,21 @@ var AFSRuntime = class {
27
28
  async mount(provider, mountPath, options) {
28
29
  const namespace = options?.namespace ?? null;
29
30
  this.afs.mount(provider, mountPath, { namespace });
31
+ this.mountedModules.push(provider);
32
+ }
33
+ /**
34
+ * Close the runtime and disconnect all mounted providers.
35
+ * This should be called before process exit to ensure proper cleanup.
36
+ */
37
+ async close() {
38
+ const disconnectPromises = this.mountedModules.map(async (module) => {
39
+ try {
40
+ if ("onUnmount" in module && typeof module.onUnmount === "function") await module.onUnmount();
41
+ if ("disconnect" in module && typeof module.disconnect === "function") await module.disconnect();
42
+ } catch {}
43
+ });
44
+ await Promise.all(disconnectPromises);
45
+ this.mountedModules = [];
30
46
  }
31
47
  /**
32
48
  * Get all mounts, optionally filtered by namespace
@@ -73,6 +89,14 @@ var AFSRuntime = class {
73
89
  const canonicalPath = this.translatePath(path);
74
90
  return this.afs.delete(canonicalPath, options);
75
91
  }
92
+ async exec(path, args = {}, options) {
93
+ const canonicalPath = this.translatePath(path);
94
+ return this.afs.exec(canonicalPath, args, options ?? {});
95
+ }
96
+ async stat(path, options) {
97
+ const canonicalPath = this.translatePath(path);
98
+ return this.afs.stat(canonicalPath, options);
99
+ }
76
100
  };
77
101
  /**
78
102
  * Create an AFS runtime from configuration
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.mjs","names":[],"sources":["../src/runtime.ts"],"sourcesContent":["import type {\n AFSDeleteOptions,\n AFSListOptions,\n AFSModule,\n AFSReadOptions,\n AFSRoot,\n AFSSearchOptions,\n AFSWriteEntryPayload,\n AFSWriteOptions,\n MountInfo,\n} from \"@aigne/afs\";\nimport { AFS } from \"@aigne/afs\";\nimport { ConfigLoader } from \"./config/loader.js\";\nimport { createProvider } from \"./config/provider-factory.js\";\nimport { cliPathToCanonical } from \"./path-utils.js\";\n\nexport interface RuntimeOptions {\n /** Custom config loader (for testing) */\n configLoader?: ConfigLoader;\n}\n\nexport interface RuntimeMountOptions {\n /** Namespace to mount into (undefined for default namespace) */\n namespace?: string;\n}\n\n/**\n * AFS Runtime wrapper that provides namespace and CLI path support\n *\n * This runtime:\n * - Supports mounting modules with namespaces\n * - Accepts CLI UX paths (/path, @namespace/path) and converts to canonical\n * - Passes through canonical paths ($afs/path, $afs:namespace/path)\n */\nexport class AFSRuntime implements Pick<AFSRoot, \"list\" | \"read\" | \"write\" | \"search\" | \"delete\"> {\n private afs: AFS;\n\n constructor() {\n this.afs = new AFS();\n }\n\n /**\n * Mount a provider at a path in a namespace\n *\n * @param provider - The provider module to mount\n * @param mountPath - The path to mount at\n * @param options - Mount options including namespace\n */\n async mount(\n provider: AFSModule,\n mountPath: string,\n options?: RuntimeMountOptions,\n ): Promise<void> {\n // Convert undefined namespace to null for AFS core\n const namespace = options?.namespace ?? null;\n this.afs.mount(provider, mountPath, { namespace });\n }\n\n /**\n * Get all mounts, optionally filtered by namespace\n */\n getMounts(namespace?: string | null): MountInfo[] {\n return this.afs.getMounts(namespace);\n }\n\n /**\n * Get all defined namespaces\n */\n getNamespaces(): (string | null)[] {\n return this.afs.getNamespaces();\n }\n\n /**\n * Check if a path is mounted in a namespace\n */\n isMounted(path: string, namespace?: string | null): boolean {\n return this.afs.isMounted(path, namespace);\n }\n\n /**\n * Translate CLI path to canonical path\n * Supports: /path, @namespace/path, $afs/path, $afs:namespace/path\n */\n private translatePath(cliPath: string): string {\n return cliPathToCanonical(cliPath);\n }\n\n async list(path: string, options?: AFSListOptions) {\n const canonicalPath = this.translatePath(path);\n return this.afs.list(canonicalPath, options);\n }\n\n async read(path: string, options?: AFSReadOptions) {\n const canonicalPath = this.translatePath(path);\n return this.afs.read(canonicalPath, options);\n }\n\n async write(path: string, content: AFSWriteEntryPayload, options?: AFSWriteOptions) {\n const canonicalPath = this.translatePath(path);\n return this.afs.write(canonicalPath, content, options);\n }\n\n async search(path: string, query: string, options?: AFSSearchOptions) {\n const canonicalPath = this.translatePath(path);\n return this.afs.search(canonicalPath, query, options);\n }\n\n async delete(path: string, options?: AFSDeleteOptions) {\n const canonicalPath = this.translatePath(path);\n return this.afs.delete(canonicalPath, options);\n }\n}\n\n/**\n * Create an AFS runtime from configuration\n *\n * @param cwd - Working directory to load config from\n * @param options - Runtime options\n * @returns Configured AFS runtime\n */\nexport async function createRuntime(\n cwd: string = process.cwd(),\n options: RuntimeOptions = {},\n): Promise<AFSRuntime> {\n const loader = options.configLoader ?? new ConfigLoader();\n const config = await loader.load(cwd);\n\n const runtime = new AFSRuntime();\n\n for (const mount of config.mounts) {\n const provider = await createProvider(mount);\n await runtime.mount(provider, mount.path, { namespace: mount.namespace });\n }\n\n return runtime;\n}\n"],"mappings":";;;;;;;;;;;;;;AAkCA,IAAa,aAAb,MAAkG;CAChG,AAAQ;CAER,cAAc;AACZ,OAAK,MAAM,IAAI,KAAK;;;;;;;;;CAUtB,MAAM,MACJ,UACA,WACA,SACe;EAEf,MAAM,YAAY,SAAS,aAAa;AACxC,OAAK,IAAI,MAAM,UAAU,WAAW,EAAE,WAAW,CAAC;;;;;CAMpD,UAAU,WAAwC;AAChD,SAAO,KAAK,IAAI,UAAU,UAAU;;;;;CAMtC,gBAAmC;AACjC,SAAO,KAAK,IAAI,eAAe;;;;;CAMjC,UAAU,MAAc,WAAoC;AAC1D,SAAO,KAAK,IAAI,UAAU,MAAM,UAAU;;;;;;CAO5C,AAAQ,cAAc,SAAyB;AAC7C,SAAO,mBAAmB,QAAQ;;CAGpC,MAAM,KAAK,MAAc,SAA0B;EACjD,MAAM,gBAAgB,KAAK,cAAc,KAAK;AAC9C,SAAO,KAAK,IAAI,KAAK,eAAe,QAAQ;;CAG9C,MAAM,KAAK,MAAc,SAA0B;EACjD,MAAM,gBAAgB,KAAK,cAAc,KAAK;AAC9C,SAAO,KAAK,IAAI,KAAK,eAAe,QAAQ;;CAG9C,MAAM,MAAM,MAAc,SAA+B,SAA2B;EAClF,MAAM,gBAAgB,KAAK,cAAc,KAAK;AAC9C,SAAO,KAAK,IAAI,MAAM,eAAe,SAAS,QAAQ;;CAGxD,MAAM,OAAO,MAAc,OAAe,SAA4B;EACpE,MAAM,gBAAgB,KAAK,cAAc,KAAK;AAC9C,SAAO,KAAK,IAAI,OAAO,eAAe,OAAO,QAAQ;;CAGvD,MAAM,OAAO,MAAc,SAA4B;EACrD,MAAM,gBAAgB,KAAK,cAAc,KAAK;AAC9C,SAAO,KAAK,IAAI,OAAO,eAAe,QAAQ;;;;;;;;;;AAWlD,eAAsB,cACpB,MAAc,QAAQ,KAAK,EAC3B,UAA0B,EAAE,EACP;CAErB,MAAM,SAAS,OADA,QAAQ,gBAAgB,IAAI,cAAc,EAC7B,KAAK,IAAI;CAErC,MAAM,UAAU,IAAI,YAAY;AAEhC,MAAK,MAAM,SAAS,OAAO,QAAQ;EACjC,MAAM,WAAW,MAAM,eAAe,MAAM;AAC5C,QAAM,QAAQ,MAAM,UAAU,MAAM,MAAM,EAAE,WAAW,MAAM,WAAW,CAAC;;AAG3E,QAAO"}
1
+ {"version":3,"file":"runtime.mjs","names":[],"sources":["../src/runtime.ts"],"sourcesContent":["import type {\n AFSDeleteOptions,\n AFSExecOptions,\n AFSExecResult,\n AFSListOptions,\n AFSModule,\n AFSReadOptions,\n AFSRoot,\n AFSSearchOptions,\n AFSStatOptions,\n AFSStatResult,\n AFSWriteEntryPayload,\n AFSWriteOptions,\n MountInfo,\n} from \"@aigne/afs\";\nimport { AFS } from \"@aigne/afs\";\nimport { ConfigLoader } from \"./config/loader.js\";\nimport { createProvider } from \"./config/provider-factory.js\";\nimport { cliPathToCanonical } from \"./path-utils.js\";\n\nexport interface RuntimeOptions {\n /** Custom config loader (for testing) */\n configLoader?: ConfigLoader;\n}\n\nexport interface RuntimeMountOptions {\n /** Namespace to mount into (undefined for default namespace) */\n namespace?: string;\n}\n\n/**\n * AFS Runtime wrapper that provides namespace and CLI path support\n *\n * This runtime:\n * - Supports mounting modules with namespaces\n * - Accepts CLI UX paths (/path, @namespace/path) and converts to canonical\n * - Passes through canonical paths ($afs/path, $afs:namespace/path)\n */\nexport class AFSRuntime implements Pick<AFSRoot, \"list\" | \"read\" | \"write\" | \"search\" | \"delete\"> {\n private afs: AFS;\n private mountedModules: AFSModule[] = [];\n\n constructor() {\n this.afs = new AFS();\n }\n\n /**\n * Mount a provider at a path in a namespace\n *\n * @param provider - The provider module to mount\n * @param mountPath - The path to mount at\n * @param options - Mount options including namespace\n */\n async mount(\n provider: AFSModule,\n mountPath: string,\n options?: RuntimeMountOptions,\n ): Promise<void> {\n // Convert undefined namespace to null for AFS core\n const namespace = options?.namespace ?? null;\n this.afs.mount(provider, mountPath, { namespace });\n this.mountedModules.push(provider);\n }\n\n /**\n * Close the runtime and disconnect all mounted providers.\n * This should be called before process exit to ensure proper cleanup.\n */\n async close(): Promise<void> {\n // Disconnect all modules that have onUnmount or disconnect methods\n const disconnectPromises = this.mountedModules.map(async (module) => {\n try {\n // Check for onUnmount (standard AFS lifecycle method)\n if (\"onUnmount\" in module && typeof module.onUnmount === \"function\") {\n await module.onUnmount();\n }\n // Check for disconnect (MCP provider specific)\n if (\"disconnect\" in module && typeof module.disconnect === \"function\") {\n await (module as { disconnect: () => Promise<void> }).disconnect();\n }\n } catch {\n // Ignore errors during cleanup - the process is exiting anyway\n }\n });\n\n await Promise.all(disconnectPromises);\n this.mountedModules = [];\n }\n\n /**\n * Get all mounts, optionally filtered by namespace\n */\n getMounts(namespace?: string | null): MountInfo[] {\n return this.afs.getMounts(namespace);\n }\n\n /**\n * Get all defined namespaces\n */\n getNamespaces(): (string | null)[] {\n return this.afs.getNamespaces();\n }\n\n /**\n * Check if a path is mounted in a namespace\n */\n isMounted(path: string, namespace?: string | null): boolean {\n return this.afs.isMounted(path, namespace);\n }\n\n /**\n * Translate CLI path to canonical path\n * Supports: /path, @namespace/path, $afs/path, $afs:namespace/path\n */\n private translatePath(cliPath: string): string {\n return cliPathToCanonical(cliPath);\n }\n\n async list(path: string, options?: AFSListOptions) {\n const canonicalPath = this.translatePath(path);\n return this.afs.list(canonicalPath, options);\n }\n\n async read(path: string, options?: AFSReadOptions) {\n const canonicalPath = this.translatePath(path);\n return this.afs.read(canonicalPath, options);\n }\n\n async write(path: string, content: AFSWriteEntryPayload, options?: AFSWriteOptions) {\n const canonicalPath = this.translatePath(path);\n return this.afs.write(canonicalPath, content, options);\n }\n\n async search(path: string, query: string, options?: AFSSearchOptions) {\n const canonicalPath = this.translatePath(path);\n return this.afs.search(canonicalPath, query, options);\n }\n\n async delete(path: string, options?: AFSDeleteOptions) {\n const canonicalPath = this.translatePath(path);\n return this.afs.delete(canonicalPath, options);\n }\n\n async exec(\n path: string,\n args: Record<string, any> = {},\n options?: AFSExecOptions,\n ): Promise<AFSExecResult> {\n const canonicalPath = this.translatePath(path);\n return this.afs.exec(canonicalPath, args, options ?? {});\n }\n\n async stat(path: string, options?: AFSStatOptions): Promise<AFSStatResult> {\n const canonicalPath = this.translatePath(path);\n return this.afs.stat(canonicalPath, options);\n }\n}\n\n/**\n * Create an AFS runtime from configuration\n *\n * @param cwd - Working directory to load config from\n * @param options - Runtime options\n * @returns Configured AFS runtime\n */\nexport async function createRuntime(\n cwd: string = process.cwd(),\n options: RuntimeOptions = {},\n): Promise<AFSRuntime> {\n const loader = options.configLoader ?? new ConfigLoader();\n const config = await loader.load(cwd);\n\n const runtime = new AFSRuntime();\n\n for (const mount of config.mounts) {\n const provider = await createProvider(mount);\n await runtime.mount(provider, mount.path, { namespace: mount.namespace });\n }\n\n return runtime;\n}\n"],"mappings":";;;;;;;;;;;;;;AAsCA,IAAa,aAAb,MAAkG;CAChG,AAAQ;CACR,AAAQ,iBAA8B,EAAE;CAExC,cAAc;AACZ,OAAK,MAAM,IAAI,KAAK;;;;;;;;;CAUtB,MAAM,MACJ,UACA,WACA,SACe;EAEf,MAAM,YAAY,SAAS,aAAa;AACxC,OAAK,IAAI,MAAM,UAAU,WAAW,EAAE,WAAW,CAAC;AAClD,OAAK,eAAe,KAAK,SAAS;;;;;;CAOpC,MAAM,QAAuB;EAE3B,MAAM,qBAAqB,KAAK,eAAe,IAAI,OAAO,WAAW;AACnE,OAAI;AAEF,QAAI,eAAe,UAAU,OAAO,OAAO,cAAc,WACvD,OAAM,OAAO,WAAW;AAG1B,QAAI,gBAAgB,UAAU,OAAO,OAAO,eAAe,WACzD,OAAO,OAA+C,YAAY;WAE9D;IAGR;AAEF,QAAM,QAAQ,IAAI,mBAAmB;AACrC,OAAK,iBAAiB,EAAE;;;;;CAM1B,UAAU,WAAwC;AAChD,SAAO,KAAK,IAAI,UAAU,UAAU;;;;;CAMtC,gBAAmC;AACjC,SAAO,KAAK,IAAI,eAAe;;;;;CAMjC,UAAU,MAAc,WAAoC;AAC1D,SAAO,KAAK,IAAI,UAAU,MAAM,UAAU;;;;;;CAO5C,AAAQ,cAAc,SAAyB;AAC7C,SAAO,mBAAmB,QAAQ;;CAGpC,MAAM,KAAK,MAAc,SAA0B;EACjD,MAAM,gBAAgB,KAAK,cAAc,KAAK;AAC9C,SAAO,KAAK,IAAI,KAAK,eAAe,QAAQ;;CAG9C,MAAM,KAAK,MAAc,SAA0B;EACjD,MAAM,gBAAgB,KAAK,cAAc,KAAK;AAC9C,SAAO,KAAK,IAAI,KAAK,eAAe,QAAQ;;CAG9C,MAAM,MAAM,MAAc,SAA+B,SAA2B;EAClF,MAAM,gBAAgB,KAAK,cAAc,KAAK;AAC9C,SAAO,KAAK,IAAI,MAAM,eAAe,SAAS,QAAQ;;CAGxD,MAAM,OAAO,MAAc,OAAe,SAA4B;EACpE,MAAM,gBAAgB,KAAK,cAAc,KAAK;AAC9C,SAAO,KAAK,IAAI,OAAO,eAAe,OAAO,QAAQ;;CAGvD,MAAM,OAAO,MAAc,SAA4B;EACrD,MAAM,gBAAgB,KAAK,cAAc,KAAK;AAC9C,SAAO,KAAK,IAAI,OAAO,eAAe,QAAQ;;CAGhD,MAAM,KACJ,MACA,OAA4B,EAAE,EAC9B,SACwB;EACxB,MAAM,gBAAgB,KAAK,cAAc,KAAK;AAC9C,SAAO,KAAK,IAAI,KAAK,eAAe,MAAM,WAAW,EAAE,CAAC;;CAG1D,MAAM,KAAK,MAAc,SAAkD;EACzE,MAAM,gBAAgB,KAAK,cAAc,KAAK;AAC9C,SAAO,KAAK,IAAI,KAAK,eAAe,QAAQ;;;;;;;;;;AAWhD,eAAsB,cACpB,MAAc,QAAQ,KAAK,EAC3B,UAA0B,EAAE,EACP;CAErB,MAAM,SAAS,OADA,QAAQ,gBAAgB,IAAI,cAAc,EAC7B,KAAK,IAAI;CAErC,MAAM,UAAU,IAAI,YAAY;AAEhC,MAAK,MAAM,SAAS,OAAO,QAAQ;EACjC,MAAM,WAAW,MAAM,eAAe,MAAM;AAC5C,QAAM,QAAQ,MAAM,UAAU,MAAM,MAAM,EAAE,WAAW,MAAM,WAAW,CAAC;;AAG3E,QAAO"}
@@ -45,16 +45,7 @@ function formatHeader(options) {
45
45
  function printHeader(options) {
46
46
  if (shouldShowHeader()) console.log(formatHeader(options));
47
47
  }
48
- /**
49
- * Print just the logo with tagline (for exit messages etc.)
50
- */
51
- function printLogo() {
52
- const coloredLogo = require_terminal.colors.brightCyan(LOGO);
53
- const coloredTagline = require_terminal.colors.dim(TAGLINE);
54
- console.log(`${coloredLogo}${coloredTagline}`);
55
- }
56
48
 
57
49
  //#endregion
58
50
  exports.printHeader = printHeader;
59
- exports.printLogo = printLogo;
60
51
  exports.shouldShowHeader = shouldShowHeader;
@@ -45,15 +45,7 @@ function formatHeader(options) {
45
45
  function printHeader(options) {
46
46
  if (shouldShowHeader()) console.log(formatHeader(options));
47
47
  }
48
- /**
49
- * Print just the logo with tagline (for exit messages etc.)
50
- */
51
- function printLogo() {
52
- const coloredLogo = colors.brightCyan(LOGO);
53
- const coloredTagline = colors.dim(TAGLINE);
54
- console.log(`${coloredLogo}${coloredTagline}`);
55
- }
56
48
 
57
49
  //#endregion
58
- export { printHeader, printLogo, shouldShowHeader };
50
+ export { printHeader, shouldShowHeader };
59
51
  //# sourceMappingURL=header.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"header.mjs","names":[],"sources":["../../src/ui/header.ts"],"sourcesContent":["/**\n * CLI Header and Branding\n *\n * Displays the AFS logo, version, and status information\n * in interactive (human) mode.\n */\n\nimport { colors, isHeaderDisabled, isTTY } from \"./terminal.js\";\n\n/**\n * ASCII art logo for AFS\n * Clean, compact design using Unicode block characters\n */\nconst LOGO = `\n▄▀█ █▀▀ █▀\n█▀█ █▀░ ▄█\n`.trimStart();\n\n/**\n * Tagline displayed below the logo\n */\nconst TAGLINE = \"Agentic File System\";\n\n/**\n * Options for header display\n */\nexport interface HeaderOptions {\n version: string;\n mountCount: number;\n}\n\n/**\n * Check if header should be displayed\n * Header is shown when:\n * - stdout is a TTY\n * - AFS_NO_HEADER is not set\n */\nexport function shouldShowHeader(): boolean {\n return isTTY() && !isHeaderDisabled();\n}\n\n/**\n * Format the CLI header with logo, version, and status\n */\nexport function formatHeader(options: HeaderOptions): string {\n const { version, mountCount } = options;\n\n // Colorize the logo\n const coloredLogo = colors.brightCyan(LOGO);\n\n // Colorize the tagline\n const coloredTagline = colors.dim(TAGLINE);\n\n // Format status line\n const versionPart = colors.green(`v${version}`);\n const mountPart = colors.yellow(`${mountCount} ${mountCount === 1 ? \"mount\" : \"mounts\"}`);\n const statusLine = `${versionPart} ${colors.dim(\"•\")} ${mountPart}`;\n\n return `${coloredLogo}${coloredTagline}\\n\\n${statusLine}\\n`;\n}\n\n/**\n * Print the header to stdout if conditions are met\n */\nexport function printHeader(options: HeaderOptions): void {\n if (shouldShowHeader()) {\n console.log(formatHeader(options));\n }\n}\n\n/**\n * Print just the logo with tagline (for exit messages etc.)\n */\nexport function printLogo(): void {\n const coloredLogo = colors.brightCyan(LOGO);\n const coloredTagline = colors.dim(TAGLINE);\n console.log(`${coloredLogo}${coloredTagline}`);\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,MAAM,OAAO;;;EAGX,WAAW;;;;AAKb,MAAM,UAAU;;;;;;;AAgBhB,SAAgB,mBAA4B;AAC1C,QAAO,OAAO,IAAI,CAAC,kBAAkB;;;;;AAMvC,SAAgB,aAAa,SAAgC;CAC3D,MAAM,EAAE,SAAS,eAAe;CAGhC,MAAM,cAAc,OAAO,WAAW,KAAK;CAG3C,MAAM,iBAAiB,OAAO,IAAI,QAAQ;CAG1C,MAAM,cAAc,OAAO,MAAM,IAAI,UAAU;CAC/C,MAAM,YAAY,OAAO,OAAO,GAAG,WAAW,GAAG,eAAe,IAAI,UAAU,WAAW;AAGzF,QAAO,GAAG,cAAc,eAAe,MAFpB,GAAG,YAAY,GAAG,OAAO,IAAI,IAAI,CAAC,GAAG,YAEA;;;;;AAM1D,SAAgB,YAAY,SAA8B;AACxD,KAAI,kBAAkB,CACpB,SAAQ,IAAI,aAAa,QAAQ,CAAC;;;;;AAOtC,SAAgB,YAAkB;CAChC,MAAM,cAAc,OAAO,WAAW,KAAK;CAC3C,MAAM,iBAAiB,OAAO,IAAI,QAAQ;AAC1C,SAAQ,IAAI,GAAG,cAAc,iBAAiB"}
1
+ {"version":3,"file":"header.mjs","names":[],"sources":["../../src/ui/header.ts"],"sourcesContent":["/**\n * CLI Header and Branding\n *\n * Displays the AFS logo, version, and status information\n * in interactive (human) mode.\n */\n\nimport { colors, isHeaderDisabled, isTTY } from \"./terminal.js\";\n\n/**\n * ASCII art logo for AFS\n * Clean, compact design using Unicode block characters\n */\nconst LOGO = `\n▄▀█ █▀▀ █▀\n█▀█ █▀░ ▄█\n`.trimStart();\n\n/**\n * Tagline displayed below the logo\n */\nconst TAGLINE = \"Agentic File System\";\n\n/**\n * Options for header display\n */\nexport interface HeaderOptions {\n version: string;\n mountCount: number;\n}\n\n/**\n * Check if header should be displayed\n * Header is shown when:\n * - stdout is a TTY\n * - AFS_NO_HEADER is not set\n */\nexport function shouldShowHeader(): boolean {\n return isTTY() && !isHeaderDisabled();\n}\n\n/**\n * Format the CLI header with logo, version, and status\n */\nexport function formatHeader(options: HeaderOptions): string {\n const { version, mountCount } = options;\n\n // Colorize the logo\n const coloredLogo = colors.brightCyan(LOGO);\n\n // Colorize the tagline\n const coloredTagline = colors.dim(TAGLINE);\n\n // Format status line\n const versionPart = colors.green(`v${version}`);\n const mountPart = colors.yellow(`${mountCount} ${mountCount === 1 ? \"mount\" : \"mounts\"}`);\n const statusLine = `${versionPart} ${colors.dim(\"•\")} ${mountPart}`;\n\n return `${coloredLogo}${coloredTagline}\\n\\n${statusLine}\\n`;\n}\n\n/**\n * Print the header to stdout if conditions are met\n */\nexport function printHeader(options: HeaderOptions): void {\n if (shouldShowHeader()) {\n console.log(formatHeader(options));\n }\n}\n\n/**\n * Print just the logo with tagline (for exit messages etc.)\n */\nexport function printLogo(): void {\n const coloredLogo = colors.brightCyan(LOGO);\n const coloredTagline = colors.dim(TAGLINE);\n console.log(`${coloredLogo}${coloredTagline}`);\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,MAAM,OAAO;;;EAGX,WAAW;;;;AAKb,MAAM,UAAU;;;;;;;AAgBhB,SAAgB,mBAA4B;AAC1C,QAAO,OAAO,IAAI,CAAC,kBAAkB;;;;;AAMvC,SAAgB,aAAa,SAAgC;CAC3D,MAAM,EAAE,SAAS,eAAe;CAGhC,MAAM,cAAc,OAAO,WAAW,KAAK;CAG3C,MAAM,iBAAiB,OAAO,IAAI,QAAQ;CAG1C,MAAM,cAAc,OAAO,MAAM,IAAI,UAAU;CAC/C,MAAM,YAAY,OAAO,OAAO,GAAG,WAAW,GAAG,eAAe,IAAI,UAAU,WAAW;AAGzF,QAAO,GAAG,cAAc,eAAe,MAFpB,GAAG,YAAY,GAAG,OAAO,IAAI,IAAI,CAAC,GAAG,YAEA;;;;;AAM1D,SAAgB,YAAY,SAA8B;AACxD,KAAI,kBAAkB,CACpB,SAAQ,IAAI,aAAa,QAAQ,CAAC"}
package/dist/ui/index.cjs CHANGED
@@ -7,11 +7,9 @@ const require_header = require('./header.cjs');
7
7
  */
8
8
  const colors = require_terminal.colors;
9
9
  const printHeader = require_header.printHeader;
10
- const printLogo = require_header.printLogo;
11
10
  const shouldShowHeader = require_header.shouldShowHeader;
12
11
 
13
12
  //#endregion
14
13
  exports.colors = colors;
15
14
  exports.printHeader = printHeader;
16
- exports.printLogo = printLogo;
17
15
  exports.shouldShowHeader = shouldShowHeader;
package/dist/ui/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { colors as colors$1 } from "./terminal.mjs";
2
- import { printHeader as printHeader$1, printLogo as printLogo$1, shouldShowHeader as shouldShowHeader$1 } from "./header.mjs";
2
+ import { printHeader as printHeader$1, shouldShowHeader as shouldShowHeader$1 } from "./header.mjs";
3
3
 
4
4
  //#region src/ui/index.ts
5
5
  /**
@@ -7,9 +7,8 @@ import { printHeader as printHeader$1, printLogo as printLogo$1, shouldShowHeade
7
7
  */
8
8
  const colors = colors$1;
9
9
  const printHeader = printHeader$1;
10
- const printLogo = printLogo$1;
11
10
  const shouldShowHeader = shouldShowHeader$1;
12
11
 
13
12
  //#endregion
14
- export { colors, printHeader, printLogo, shouldShowHeader };
13
+ export { colors, printHeader, shouldShowHeader };
15
14
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["_colors","_printHeader","_printLogo","_shouldShowHeader"],"sources":["../../src/ui/index.ts"],"sourcesContent":["/**\n * UI utilities for CLI output\n */\n\n// Import and re-export header utilities\nimport {\n formatHeader as _formatHeader,\n printHeader as _printHeader,\n printLogo as _printLogo,\n shouldShowHeader as _shouldShowHeader,\n type HeaderOptions,\n} from \"./header.js\";\n// Import and re-export terminal utilities\nimport {\n colors as _colors,\n isColorDisabled as _isColorDisabled,\n isHeaderDisabled as _isHeaderDisabled,\n isTTY as _isTTY,\n shouldUseColors as _shouldUseColors,\n} from \"./terminal.js\";\n\nexport const colors = _colors;\nexport const isColorDisabled = _isColorDisabled;\nexport const isHeaderDisabled = _isHeaderDisabled;\nexport const isTTY = _isTTY;\nexport const shouldUseColors = _shouldUseColors;\nexport const formatHeader = _formatHeader;\nexport const printHeader = _printHeader;\nexport const printLogo = _printLogo;\nexport const shouldShowHeader = _shouldShowHeader;\nexport type { HeaderOptions };\n"],"mappings":";;;;;;;AAqBA,MAAa,SAASA;AAMtB,MAAa,cAAcC;AAC3B,MAAa,YAAYC;AACzB,MAAa,mBAAmBC"}
1
+ {"version":3,"file":"index.mjs","names":["_colors","_printHeader","_shouldShowHeader"],"sources":["../../src/ui/index.ts"],"sourcesContent":["/**\n * UI utilities for CLI output\n */\n\n// Import and re-export header utilities\nimport {\n formatHeader as _formatHeader,\n printHeader as _printHeader,\n printLogo as _printLogo,\n shouldShowHeader as _shouldShowHeader,\n type HeaderOptions,\n} from \"./header.js\";\n// Import and re-export terminal utilities\nimport {\n colors as _colors,\n isColorDisabled as _isColorDisabled,\n isHeaderDisabled as _isHeaderDisabled,\n isTTY as _isTTY,\n shouldUseColors as _shouldUseColors,\n} from \"./terminal.js\";\n\nexport const colors = _colors;\nexport const isColorDisabled = _isColorDisabled;\nexport const isHeaderDisabled = _isHeaderDisabled;\nexport const isTTY = _isTTY;\nexport const shouldUseColors = _shouldUseColors;\nexport const formatHeader = _formatHeader;\nexport const printHeader = _printHeader;\nexport const printLogo = _printLogo;\nexport const shouldShowHeader = _shouldShowHeader;\nexport type { HeaderOptions };\n"],"mappings":";;;;;;;AAqBA,MAAa,SAASA;AAMtB,MAAa,cAAcC;AAE3B,MAAa,mBAAmBC"}