@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.
- package/dist/cli.cjs +41 -18
- package/dist/cli.mjs +42 -19
- package/dist/cli.mjs.map +1 -1
- package/dist/commands/exec.cjs +132 -14
- package/dist/commands/exec.mjs +129 -14
- package/dist/commands/exec.mjs.map +1 -1
- package/dist/commands/explain.cjs +1 -1
- package/dist/commands/explain.mjs +1 -1
- package/dist/commands/explain.mjs.map +1 -1
- package/dist/commands/index.mjs +1 -1
- package/dist/commands/ls.cjs +129 -30
- package/dist/commands/ls.mjs +129 -30
- package/dist/commands/ls.mjs.map +1 -1
- package/dist/commands/read.cjs +213 -14
- package/dist/commands/read.mjs +213 -14
- package/dist/commands/read.mjs.map +1 -1
- package/dist/commands/stat.cjs +116 -34
- package/dist/commands/stat.mjs +117 -34
- package/dist/commands/stat.mjs.map +1 -1
- package/dist/commands/write.cjs +37 -4
- package/dist/commands/write.mjs +38 -4
- package/dist/commands/write.mjs.map +1 -1
- package/dist/config/loader.cjs +12 -1
- package/dist/config/loader.mjs +12 -1
- package/dist/config/loader.mjs.map +1 -1
- package/dist/config/provider-factory.cjs +310 -3
- package/dist/config/provider-factory.mjs +310 -3
- package/dist/config/provider-factory.mjs.map +1 -1
- package/dist/config/uri-parser.cjs +195 -2
- package/dist/config/uri-parser.mjs +195 -2
- package/dist/config/uri-parser.mjs.map +1 -1
- package/dist/explorer/actions.cjs +53 -23
- package/dist/explorer/actions.mjs +54 -23
- package/dist/explorer/actions.mjs.map +1 -1
- package/dist/explorer/components/dialog.cjs +163 -10
- package/dist/explorer/components/dialog.mjs +163 -10
- package/dist/explorer/components/dialog.mjs.map +1 -1
- package/dist/explorer/components/file-list.mjs.map +1 -1
- package/dist/explorer/components/metadata-panel.cjs +39 -25
- package/dist/explorer/components/metadata-panel.mjs +39 -25
- package/dist/explorer/components/metadata-panel.mjs.map +1 -1
- package/dist/explorer/screen.cjs +23 -8
- package/dist/explorer/screen.mjs +24 -9
- package/dist/explorer/screen.mjs.map +1 -1
- package/dist/explorer/theme.cjs +3 -1
- package/dist/explorer/theme.mjs +3 -1
- package/dist/explorer/theme.mjs.map +1 -1
- package/dist/path-utils.cjs +2 -1
- package/dist/path-utils.mjs +1 -1
- package/dist/runtime.cjs +24 -0
- package/dist/runtime.mjs +24 -0
- package/dist/runtime.mjs.map +1 -1
- package/dist/ui/header.cjs +0 -9
- package/dist/ui/header.mjs +1 -9
- package/dist/ui/header.mjs.map +1 -1
- package/dist/ui/index.cjs +0 -2
- package/dist/ui/index.mjs +2 -3
- package/dist/ui/index.mjs.map +1 -1
- package/dist/utils/meta.cjs +51 -0
- package/dist/utils/meta.mjs +49 -0
- package/dist/utils/meta.mjs.map +1 -0
- 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(`
|
|
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 (
|
|
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 //
|
|
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":"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"}
|
package/dist/explorer/screen.cjs
CHANGED
|
@@ -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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
134
|
-
require_index.
|
|
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
|
-
|
|
138
|
-
|
|
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
|
},
|
package/dist/explorer/screen.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { colors
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
132
|
-
|
|
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
|
-
|
|
136
|
-
|
|
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":";;;;;;;;;;;;;;;;;;;;;AAsCA,eAAsB,qBAAqB,SAA+C;CACxF,MAAM,EAAE,SAAS,YAAY,KAAK,SAAS,eAAe;CAG1D,MAAM,QAAQ,YAAY,mBAAmB,UAAU,CAAC;CAGxD,MAAM,WAAW,uBAAuB;CAIxC,MAAM,SAAS,QAAQ,OAAO;EAC5B,UAAU;EACV,OAAO;EACP,UAAU;EACV,aAAa;EACb,UAAU;EACX,CAAC;AAGiB,iBAAgB,SAAS;EAC1C,QAAQ;EACR;EACA,OAAO;EACP,KAAK;EACN,CAAC;CAEF,MAAM,WAAW,eAAe,SAAS;EACvC,QAAQ;EACR;EACA,OAAO;EACP,QAAQ;EACR,KAAK;EACL,MAAM;EACP,CAAC;CAEF,MAAM,gBAAgB,oBAAoB,SAAS;EACjD,QAAQ;EACR,OAAO;EACP,QAAQ;EACR,KAAK;EACL,OAAO;EACR,CAAC;AAEmB,mBAAkB,SAAS;EAC9C,QAAQ;EACR;EACA,OAAO;EACP,QAAQ;EACT,CAAC;CAEF,MAAM,UAAU,oBAAoB,SAAS,EAC3C,QAAQ,QACT,CAAC;CAGF,eAAe,SAAS,MAA6B;AACnD,QAAM,SAAS;GAAE,SAAS;GAAM,OAAO;GAAW,CAAC;EAEnD,MAAM,SAAS,MAAM,cAAc,SAAS,KAAK;AAEjD,QAAM,SAAS;GACb,aAAa;GACb,SAAS,OAAO;GAChB,eAAe;GACf,cAAc;GACd,SAAS;GACT,OAAO,OAAO;GACf,CAAC;AAGF,kBAAgB;;CAIlB,eAAe,iBAAgC;EAC7C,MAAM,WAAW,WAAW,YAAY,MAAM,UAAU,CAAC;AACzD,MAAI,UAAU;GACZ,MAAM,WAAW,MAAM,aAAa,SAAS,SAAS;AACtD,iBAAc,OAAO,UAAU,SAAS;QAExC,eAAc,OAAO;;CAKzB,eAAe,cAA6B;EAC1C,MAAM,WAAW,WAAW,YAAY,MAAM,UAAU,CAAC;AACzD,MAAI,CAAC,SAAU;AAEf,MAAI,SAAS,SAAS,eAAe,SAAS,SAAS,KACrD,OAAM,SAAS,SAAS,KAAK;WACpB,SAAS,SAAS,OAC3B,OAAM,YAAY;;CAKtB,eAAe,aAA4B;EACzC,MAAM,QAAQ,MAAM,UAAU;EAC9B,MAAM,aAAa,WAAW,cAAc,MAAM,YAAY;AAC9D,MAAI,eAAe,MAAM,YACvB,OAAM,SAAS,WAAW;;CAK9B,eAAe,aAA4B;EACzC,MAAM,WAAW,WAAW,YAAY,MAAM,UAAU,CAAC;AACzD,MAAI,CAAC,YAAY,SAAS,SAAS,KAAM;AAEzC,MAAI,SAAS,SAAS,QAAQ;AAC5B,WAAQ,YAAY,eAAe;GACnC,MAAM,SAAS,MAAM,gBAAgB,SAAS,SAAS,KAAK;AAC5D,OAAI,OAAO,MACT,SAAQ,UAAU,cAAc,OAAO,MAAM;OAE7C,SAAQ,aAAa,SAAS,MAAM,OAAO,QAAQ;aAE5C,SAAS,SAAS,YAE3B,OAAM,SAAS,SAAS,KAAK;;CAKjC,eAAe,gBAA+B;EAC5C,MAAM,WAAW,WAAW,YAAY,MAAM,UAAU,CAAC;AACzD,MAAI,CAAC,YAAY,SAAS,SAAS,KAAM;AAEzC,UAAQ,YAAY,uBAAuB;EAC3C,MAAM,SAAS,MAAM,WAAW,SAAS,SAAS,KAAK;AACvD,MAAI,OAAO,MACT,SAAQ,UAAU,iBAAiB,OAAO,MAAM;MAEhD,SAAQ,YAAY,SAAS,MAAM,OAAO,QAAQ;;CAKtD,eAAe,aAA4B;EACzC,MAAM,WAAW,WAAW,YAAY,MAAM,UAAU,CAAC;AACzD,MAAI,CAAC,YAAY,SAAS,SAAS,KAAM;AAEzC,UAAQ,YAAY,mBAAmB;EACvC,MAAM,SAAS,MAAM,cAAc,SAAS,SAAS,MAAM,UAAU;AACrE,UAAQ,iBAAiB,WAAW,OAAO,SAAS,OAAO,SAAS,OAAO,KAAK;;CAIlF,eAAe,gBAA+B;AAE5C,QAAM,SADQ,MAAM,UAAU,CACT,YAAY;;CAInC,MAAM,iBAA6D;EACjE,YAAY,QAAQ,SAAS,SAAS;EACtC,SAAS;EACT,MAAM;EACN,MAAM;EACN,SAAS;EACT,YAAY;AACV,WAAQ,YAAY,wCAAwC;AAC1D,WAAO,SAAS;AAEhB,eAAW;IACX,MAAM,cAAc,OAAO,MAAM,IAAI,UAAU;IAC/C,MAAM,YAAY,OAAO,OAAO,GAAG,WAAW,GAAG,eAAe,IAAI,UAAU,WAAW;AACzF,YAAQ,IAAI,GAAG,YAAY,GAAG,OAAO,IAAI,IAAI,CAAC,GAAG,YAAY;AAC7D,YAAQ,IAAI,OAAO,IAAI,qCAAqC,CAAC;AAC7D,YAAQ,KAAK,EAAE;KACf;;EAEJ,gBAAgB;AACd,SAAM,SAAS,WAAW,GAAG,MAAM,UAAU,CAAC,CAAC;AAC/C,mBAAgB;;EAElB,kBAAkB;AAChB,SAAM,SAAS,WAAW,KAAK,MAAM,UAAU,CAAC,CAAC;AACjD,mBAAgB;;EAElB,aAAa;EACb,YAAY;EACZ,kBAAkB;AAChB,SAAM,SAAS,WAAW,KAAK,MAAM,UAAU,CAAC,CAAC;AACjD,mBAAgB;;EAElB,iBAAiB;AACf,SAAM,SAAS,WAAW,IAAI,MAAM,UAAU,CAAC,CAAC;AAChD,mBAAgB;;EAElB,oBAAoB;AAClB,SAAM,SAAS,WAAW,OAAO,MAAM,UAAU,EAAE,SAAS,kBAAkB,CAAC,CAAC;AAChF,mBAAgB;;EAElB,sBAAsB;AACpB,SAAM,SAAS,WAAW,SAAS,MAAM,UAAU,EAAE,SAAS,kBAAkB,CAAC,CAAC;AAClF,mBAAgB;;EAElB,cAAc;AACZ,OAAI,QAAQ,QAAQ,CAClB,SAAQ,OAAO;OAGf,gBAAe,QAAQ;;EAG5B;AAGD,QAAO,GAAG,YAAY,OAAO,IAAI,QAAQ;AAEvC,MAAI,QAAQ,QAAQ,CAAE;EAEtB,MAAM,UAAU,IAAI,QAAQ;AAC5B,MAAI,CAAC,QAAS;EAGd,MAAM,UAAU,SAAS,iBAAiB,QAAQ;AAElD,MAAI,SAAS;GACX,MAAM,UAAU,eAAe,QAAQ;AACvC,OAAI,SAAS;AACX,UAAM,SAAS;AACf,WAAO,QAAQ;;;GAGnB;AAGF,QAAO,GAAG,gBAAgB;AACxB,SAAO,QAAQ;GACf;AAGF,UAAS,OAAO;AAGhB,OAAM,SAAS,UAAU;AAGzB,QAAO,QAAQ;AAGf,QAAO,IAAI,SAAS,YAAY;AAC9B,SAAO,GAAG,iBAAiB;AACzB,YAAS;IACT;GACF"}
|
|
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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"}
|
package/dist/explorer/theme.cjs
CHANGED
package/dist/explorer/theme.mjs
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/path-utils.cjs
CHANGED
package/dist/path-utils.mjs
CHANGED
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
|
package/dist/runtime.mjs.map
CHANGED
|
@@ -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":";;;;;;;;;;;;;;
|
|
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"}
|
package/dist/ui/header.cjs
CHANGED
|
@@ -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;
|
package/dist/ui/header.mjs
CHANGED
|
@@ -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,
|
|
50
|
+
export { printHeader, shouldShowHeader };
|
|
59
51
|
//# sourceMappingURL=header.mjs.map
|
package/dist/ui/header.mjs.map
CHANGED
|
@@ -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
|
|
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,
|
|
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,
|
|
13
|
+
export { colors, printHeader, shouldShowHeader };
|
|
15
14
|
//# sourceMappingURL=index.mjs.map
|
package/dist/ui/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["_colors","_printHeader","
|
|
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"}
|