web-a2e 1.0.0
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/.clangd +5 -0
- package/.mcp.json +12 -0
- package/CLAUDE.md +362 -0
- package/CMakeLists.txt +774 -0
- package/LICENSE +21 -0
- package/README.md +392 -0
- package/build-wasm/generated/roms.cpp +2447 -0
- package/docker-compose.staging.yml +9 -0
- package/docs/basic-rom-disassembly.md +6663 -0
- package/docs/softswitch-comparison.md +273 -0
- package/docs/thunderclock-debug.md +89 -0
- package/examples/cube.bas +72 -0
- package/examples/hello.s +55 -0
- package/examples/scroll.s +140 -0
- package/package.json +18 -0
- package/public/assets/apple-logo-old.png +0 -0
- package/public/assets/apple-logo.png +0 -0
- package/public/assets/drive-closed-light-on.png +0 -0
- package/public/assets/drive-closed.png +0 -0
- package/public/assets/drive-open-light-on.png +0 -0
- package/public/assets/drive-open.png +0 -0
- package/public/audio-worklet.js +82 -0
- package/public/disks/Apple DOS 3.3 January 1983.dsk +0 -0
- package/public/disks/ProDOS 2.4.3.po +0 -0
- package/public/disks/h32mb.2mg +0 -0
- package/public/disks/library.json +26 -0
- package/public/docs/llms/llm-assembler.txt +90 -0
- package/public/docs/llms/llm-basic-program.txt +256 -0
- package/public/docs/llms/llm-disk-drives.txt +72 -0
- package/public/docs/llms/llm-file-explorer.txt +50 -0
- package/public/docs/llms/llm-hard-drives.txt +80 -0
- package/public/docs/llms/llm-main.txt +51 -0
- package/public/docs/llms/llm-slot-configuration.txt +66 -0
- package/public/icons/icon-192.svg +4 -0
- package/public/icons/icon-512.svg +4 -0
- package/public/index.html +661 -0
- package/public/llms.txt +49 -0
- package/public/manifest.json +29 -0
- package/public/shaders/burnin.glsl +22 -0
- package/public/shaders/crt.glsl +706 -0
- package/public/shaders/edge.glsl +109 -0
- package/public/shaders/vertex.glsl +8 -0
- package/public/sw.js +186 -0
- package/roms/341-0027.bin +0 -0
- package/roms/341-0160-A-US-UK.bin +0 -0
- package/roms/341-0160-A.bin +0 -0
- package/roms/342-0273-A-US-UK.bin +0 -0
- package/roms/342-0349-B-C0-FF.bin +0 -0
- package/roms/Apple Mouse Interface Card ROM - 342-0270-C.bin +0 -0
- package/roms/Thunderclock Plus ROM.bin +0 -0
- package/scripts/generate_roms.sh +69 -0
- package/src/bindings/wasm_interface.cpp +1940 -0
- package/src/core/assembler/assembler.cpp +1239 -0
- package/src/core/assembler/assembler.hpp +115 -0
- package/src/core/audio/audio.cpp +160 -0
- package/src/core/audio/audio.hpp +81 -0
- package/src/core/basic/basic_detokenizer.cpp +436 -0
- package/src/core/basic/basic_detokenizer.hpp +41 -0
- package/src/core/basic/basic_tokenizer.cpp +286 -0
- package/src/core/basic/basic_tokenizer.hpp +26 -0
- package/src/core/basic/basic_tokens.hpp +295 -0
- package/src/core/cards/disk2_card.cpp +568 -0
- package/src/core/cards/disk2_card.hpp +316 -0
- package/src/core/cards/expansion_card.hpp +185 -0
- package/src/core/cards/mockingboard/ay8910.cpp +616 -0
- package/src/core/cards/mockingboard/ay8910.hpp +159 -0
- package/src/core/cards/mockingboard/via6522.cpp +530 -0
- package/src/core/cards/mockingboard/via6522.hpp +163 -0
- package/src/core/cards/mockingboard_card.cpp +312 -0
- package/src/core/cards/mockingboard_card.hpp +159 -0
- package/src/core/cards/mouse_card.cpp +654 -0
- package/src/core/cards/mouse_card.hpp +190 -0
- package/src/core/cards/smartport/block_device.cpp +202 -0
- package/src/core/cards/smartport/block_device.hpp +60 -0
- package/src/core/cards/smartport/smartport_card.cpp +603 -0
- package/src/core/cards/smartport/smartport_card.hpp +120 -0
- package/src/core/cards/thunderclock_card.cpp +237 -0
- package/src/core/cards/thunderclock_card.hpp +122 -0
- package/src/core/cpu/cpu6502.cpp +1609 -0
- package/src/core/cpu/cpu6502.hpp +203 -0
- package/src/core/debug/condition_evaluator.cpp +470 -0
- package/src/core/debug/condition_evaluator.hpp +87 -0
- package/src/core/disassembler/disassembler.cpp +552 -0
- package/src/core/disassembler/disassembler.hpp +171 -0
- package/src/core/disk-image/disk_image.hpp +267 -0
- package/src/core/disk-image/dsk_disk_image.cpp +827 -0
- package/src/core/disk-image/dsk_disk_image.hpp +204 -0
- package/src/core/disk-image/gcr_encoding.cpp +147 -0
- package/src/core/disk-image/gcr_encoding.hpp +78 -0
- package/src/core/disk-image/woz_disk_image.cpp +1049 -0
- package/src/core/disk-image/woz_disk_image.hpp +343 -0
- package/src/core/emulator.cpp +2126 -0
- package/src/core/emulator.hpp +434 -0
- package/src/core/filesystem/dos33.cpp +178 -0
- package/src/core/filesystem/dos33.hpp +66 -0
- package/src/core/filesystem/pascal.cpp +262 -0
- package/src/core/filesystem/pascal.hpp +87 -0
- package/src/core/filesystem/prodos.cpp +369 -0
- package/src/core/filesystem/prodos.hpp +119 -0
- package/src/core/input/keyboard.cpp +227 -0
- package/src/core/input/keyboard.hpp +111 -0
- package/src/core/mmu/mmu.cpp +1387 -0
- package/src/core/mmu/mmu.hpp +236 -0
- package/src/core/types.hpp +196 -0
- package/src/core/video/video.cpp +680 -0
- package/src/core/video/video.hpp +156 -0
- package/src/css/assembler-editor.css +1617 -0
- package/src/css/base.css +470 -0
- package/src/css/basic-debugger.css +791 -0
- package/src/css/basic-editor.css +792 -0
- package/src/css/controls.css +783 -0
- package/src/css/cpu-debugger.css +1413 -0
- package/src/css/debug-base.css +160 -0
- package/src/css/debug-windows.css +6455 -0
- package/src/css/disk-drives.css +406 -0
- package/src/css/documentation.css +392 -0
- package/src/css/file-explorer.css +867 -0
- package/src/css/hard-drive.css +180 -0
- package/src/css/layout.css +217 -0
- package/src/css/memory-windows.css +798 -0
- package/src/css/modals.css +510 -0
- package/src/css/monitor.css +425 -0
- package/src/css/release-notes.css +101 -0
- package/src/css/responsive.css +400 -0
- package/src/css/rule-builder.css +340 -0
- package/src/css/save-states.css +201 -0
- package/src/css/settings-windows.css +1231 -0
- package/src/css/window-switcher.css +150 -0
- package/src/js/agent/agent-manager.js +643 -0
- package/src/js/agent/agent-tools.js +293 -0
- package/src/js/agent/agent-version-tools.js +131 -0
- package/src/js/agent/assembler-tools.js +357 -0
- package/src/js/agent/basic-program-tools.js +894 -0
- package/src/js/agent/disk-tools.js +417 -0
- package/src/js/agent/file-explorer-tools.js +269 -0
- package/src/js/agent/index.js +13 -0
- package/src/js/agent/main-tools.js +222 -0
- package/src/js/agent/slot-tools.js +303 -0
- package/src/js/agent/smartport-tools.js +257 -0
- package/src/js/agent/window-tools.js +80 -0
- package/src/js/audio/audio-driver.js +417 -0
- package/src/js/audio/audio-worklet.js +85 -0
- package/src/js/audio/index.js +8 -0
- package/src/js/config/default-layout.js +34 -0
- package/src/js/config/version.js +8 -0
- package/src/js/data/apple2-rom-routines.js +577 -0
- package/src/js/debug/assembler-editor-window.js +2993 -0
- package/src/js/debug/basic-breakpoint-manager.js +529 -0
- package/src/js/debug/basic-program-parser.js +436 -0
- package/src/js/debug/basic-program-window.js +2594 -0
- package/src/js/debug/basic-variable-inspector.js +447 -0
- package/src/js/debug/breakpoint-manager.js +472 -0
- package/src/js/debug/cpu-debugger-window.js +2396 -0
- package/src/js/debug/index.js +22 -0
- package/src/js/debug/label-manager.js +238 -0
- package/src/js/debug/memory-browser-window.js +416 -0
- package/src/js/debug/memory-heat-map-window.js +481 -0
- package/src/js/debug/memory-map-window.js +206 -0
- package/src/js/debug/mockingboard-window.js +882 -0
- package/src/js/debug/mouse-card-window.js +355 -0
- package/src/js/debug/rule-builder-window.js +648 -0
- package/src/js/debug/soft-switch-window.js +458 -0
- package/src/js/debug/stack-viewer-window.js +221 -0
- package/src/js/debug/symbols.js +416 -0
- package/src/js/debug/trace-panel.js +291 -0
- package/src/js/debug/zero-page-watch-window.js +297 -0
- package/src/js/disk-manager/disk-drives-window.js +212 -0
- package/src/js/disk-manager/disk-operations.js +284 -0
- package/src/js/disk-manager/disk-persistence.js +301 -0
- package/src/js/disk-manager/disk-surface-renderer.js +388 -0
- package/src/js/disk-manager/drive-sounds.js +139 -0
- package/src/js/disk-manager/hard-drive-manager.js +481 -0
- package/src/js/disk-manager/hard-drive-persistence.js +187 -0
- package/src/js/disk-manager/hard-drive-window.js +57 -0
- package/src/js/disk-manager/index.js +890 -0
- package/src/js/display/display-settings-window.js +383 -0
- package/src/js/display/index.js +10 -0
- package/src/js/display/screen-window.js +342 -0
- package/src/js/display/webgl-renderer.js +705 -0
- package/src/js/file-explorer/disassembler.js +574 -0
- package/src/js/file-explorer/dos33.js +266 -0
- package/src/js/file-explorer/file-viewer.js +359 -0
- package/src/js/file-explorer/index.js +1261 -0
- package/src/js/file-explorer/prodos.js +549 -0
- package/src/js/file-explorer/utils.js +67 -0
- package/src/js/help/documentation-window.js +1096 -0
- package/src/js/help/index.js +10 -0
- package/src/js/help/release-notes-window.js +85 -0
- package/src/js/help/release-notes.js +612 -0
- package/src/js/input/gamepad-handler.js +176 -0
- package/src/js/input/index.js +12 -0
- package/src/js/input/input-handler.js +396 -0
- package/src/js/input/joystick-window.js +404 -0
- package/src/js/input/mouse-handler.js +99 -0
- package/src/js/input/text-selection.js +462 -0
- package/src/js/main.js +653 -0
- package/src/js/state/index.js +15 -0
- package/src/js/state/save-states-window.js +393 -0
- package/src/js/state/state-manager.js +409 -0
- package/src/js/state/state-persistence.js +218 -0
- package/src/js/ui/confirm.js +43 -0
- package/src/js/ui/disk-drive-positioner.js +347 -0
- package/src/js/ui/reminder-controller.js +129 -0
- package/src/js/ui/slot-configuration-window.js +560 -0
- package/src/js/ui/theme-manager.js +61 -0
- package/src/js/ui/toast.js +44 -0
- package/src/js/ui/ui-controller.js +897 -0
- package/src/js/ui/window-switcher.js +275 -0
- package/src/js/utils/basic-autocomplete.js +832 -0
- package/src/js/utils/basic-highlighting.js +473 -0
- package/src/js/utils/basic-tokenizer.js +153 -0
- package/src/js/utils/basic-tokens.js +117 -0
- package/src/js/utils/constants.js +28 -0
- package/src/js/utils/indexeddb-helper.js +225 -0
- package/src/js/utils/merlin-editor-support.js +905 -0
- package/src/js/utils/merlin-highlighting.js +551 -0
- package/src/js/utils/storage.js +125 -0
- package/src/js/utils/string-utils.js +19 -0
- package/src/js/utils/wasm-memory.js +54 -0
- package/src/js/windows/base-window.js +690 -0
- package/src/js/windows/index.js +9 -0
- package/src/js/windows/window-manager.js +375 -0
- package/tests/catch2/catch.hpp +17976 -0
- package/tests/common/basic_program_builder.cpp +119 -0
- package/tests/common/basic_program_builder.hpp +209 -0
- package/tests/common/disk_image_builder.cpp +444 -0
- package/tests/common/disk_image_builder.hpp +141 -0
- package/tests/common/test_helpers.hpp +118 -0
- package/tests/gcr/gcr-test.cpp +142 -0
- package/tests/integration/check-rom.js +70 -0
- package/tests/integration/compare-boot.js +239 -0
- package/tests/integration/crash-trace.js +102 -0
- package/tests/integration/disk-boot-test.js +264 -0
- package/tests/integration/memory-crash.js +108 -0
- package/tests/integration/nibble-read-test.js +249 -0
- package/tests/integration/phase-test.js +159 -0
- package/tests/integration/test_emulator.cpp +291 -0
- package/tests/integration/test_emulator_basic.cpp +91 -0
- package/tests/integration/test_emulator_debug.cpp +344 -0
- package/tests/integration/test_emulator_disk.cpp +153 -0
- package/tests/integration/test_emulator_state.cpp +163 -0
- package/tests/klaus/6502_functional_test.bin +0 -0
- package/tests/klaus/65C02_extended_opcodes_test.bin +0 -0
- package/tests/klaus/klaus_6502_test.cpp +184 -0
- package/tests/klaus/klaus_65c02_test.cpp +197 -0
- package/tests/thunderclock/thunderclock_mmu_test.cpp +304 -0
- package/tests/thunderclock/thunderclock_test.cpp +550 -0
- package/tests/unit/test_assembler.cpp +521 -0
- package/tests/unit/test_audio.cpp +196 -0
- package/tests/unit/test_ay8910.cpp +311 -0
- package/tests/unit/test_basic_detokenizer.cpp +265 -0
- package/tests/unit/test_basic_tokenizer.cpp +382 -0
- package/tests/unit/test_block_device.cpp +259 -0
- package/tests/unit/test_condition_evaluator.cpp +219 -0
- package/tests/unit/test_cpu6502.cpp +1301 -0
- package/tests/unit/test_cpu_addressing.cpp +361 -0
- package/tests/unit/test_cpu_cycle_counts.cpp +409 -0
- package/tests/unit/test_cpu_decimal.cpp +166 -0
- package/tests/unit/test_cpu_interrupts.cpp +285 -0
- package/tests/unit/test_disassembler.cpp +323 -0
- package/tests/unit/test_disk2_card.cpp +330 -0
- package/tests/unit/test_dos33.cpp +273 -0
- package/tests/unit/test_dsk_disk_image.cpp +315 -0
- package/tests/unit/test_expansion_card.cpp +178 -0
- package/tests/unit/test_gcr_encoding.cpp +232 -0
- package/tests/unit/test_keyboard.cpp +262 -0
- package/tests/unit/test_mmu.cpp +555 -0
- package/tests/unit/test_mmu_slots.cpp +323 -0
- package/tests/unit/test_mockingboard.cpp +352 -0
- package/tests/unit/test_mouse_card.cpp +386 -0
- package/tests/unit/test_pascal.cpp +248 -0
- package/tests/unit/test_prodos.cpp +259 -0
- package/tests/unit/test_smartport_card.cpp +321 -0
- package/tests/unit/test_thunderclock.cpp +354 -0
- package/tests/unit/test_via6522.cpp +323 -0
- package/tests/unit/test_video.cpp +319 -0
- package/tests/unit/test_woz_disk_image.cpp +257 -0
- package/vite.config.js +96 -0
- package/wiki/AI-Agent.md +372 -0
- package/wiki/Architecture-Overview.md +303 -0
- package/wiki/Audio-System.md +449 -0
- package/wiki/CPU-Emulation.md +477 -0
- package/wiki/Debugger.md +516 -0
- package/wiki/Disk-Drives.md +161 -0
- package/wiki/Disk-System-Internals.md +547 -0
- package/wiki/Display-Settings.md +88 -0
- package/wiki/Expansion-Slots.md +187 -0
- package/wiki/File-Explorer.md +259 -0
- package/wiki/Getting-Started.md +156 -0
- package/wiki/Home.md +69 -0
- package/wiki/Input-Devices.md +183 -0
- package/wiki/Keyboard-Shortcuts.md +158 -0
- package/wiki/Memory-System.md +364 -0
- package/wiki/Save-States.md +172 -0
- package/wiki/Video-Rendering.md +658 -0
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* disk-tools.js - Disk drive operation tools
|
|
3
|
+
*
|
|
4
|
+
* Written by
|
|
5
|
+
* Shawn Bullock <shawn@agenticexpert.ai>
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { loadDiskFromData } from "../disk-manager/disk-operations.js";
|
|
9
|
+
import { getRecentDisks, loadRecentDisk, clearRecentDisks } from "../disk-manager/disk-persistence.js";
|
|
10
|
+
|
|
11
|
+
export const diskTools = {
|
|
12
|
+
/**
|
|
13
|
+
* Insert a disk image into a drive by path
|
|
14
|
+
*/
|
|
15
|
+
driveInsertDisc: async (args) => {
|
|
16
|
+
const { driveNum = 1, path } = args;
|
|
17
|
+
|
|
18
|
+
if (!path) {
|
|
19
|
+
throw new Error("path parameter is required");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Validate driveNum (1 or 2)
|
|
23
|
+
if (driveNum !== 1 && driveNum !== 2) {
|
|
24
|
+
throw new Error("driveNum must be 1 or 2");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Validate disk format
|
|
28
|
+
const supportedFormats = ['.dsk', '.do', '.po', '.nib', '.woz'];
|
|
29
|
+
const extension = path.toLowerCase().match(/\.(dsk|do|po|nib|woz)$/);
|
|
30
|
+
|
|
31
|
+
if (!extension) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
`Unsupported disk format. Supported formats: ${supportedFormats.join(', ')}`
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Convert to 0-based index
|
|
38
|
+
const driveIndex = driveNum - 1;
|
|
39
|
+
|
|
40
|
+
// Get disk manager and WASM module
|
|
41
|
+
const diskManager = window.emulator?.diskManager;
|
|
42
|
+
const wasmModule = window.emulator?.wasmModule;
|
|
43
|
+
|
|
44
|
+
if (!diskManager) {
|
|
45
|
+
throw new Error("Disk manager not available");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!wasmModule) {
|
|
49
|
+
throw new Error("WASM module not available");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Get drive state
|
|
53
|
+
const drive = diskManager.drives[driveIndex];
|
|
54
|
+
if (!drive) {
|
|
55
|
+
throw new Error(`Drive ${driveNum} not found`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
// Call MCP tool to load disk image from filesystem
|
|
60
|
+
const agentManager = window.emulator?.agentManager;
|
|
61
|
+
if (!agentManager) {
|
|
62
|
+
throw new Error("Agent manager not available");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const result = await agentManager.callMCPTool("load_disk_image", { path });
|
|
66
|
+
|
|
67
|
+
if (!result || !result.success) {
|
|
68
|
+
throw new Error(result?.error || "Failed to load disk image");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Decode base64 to Uint8Array
|
|
72
|
+
const binaryString = atob(result.data);
|
|
73
|
+
const data = new Uint8Array(binaryString.length);
|
|
74
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
75
|
+
data[i] = binaryString.charCodeAt(i);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Use filename from MCP result
|
|
79
|
+
const filename = result.filename;
|
|
80
|
+
|
|
81
|
+
// Load disk using the disk operations helper
|
|
82
|
+
return new Promise((resolve, reject) => {
|
|
83
|
+
loadDiskFromData({
|
|
84
|
+
wasmModule,
|
|
85
|
+
drive,
|
|
86
|
+
driveNum: driveIndex,
|
|
87
|
+
filename,
|
|
88
|
+
data,
|
|
89
|
+
onSuccess: (loadedFilename) => {
|
|
90
|
+
// Update UI
|
|
91
|
+
diskManager.setDiskName(driveIndex, loadedFilename);
|
|
92
|
+
|
|
93
|
+
// Notify if callback exists
|
|
94
|
+
if (diskManager.onDiskLoaded) {
|
|
95
|
+
diskManager.onDiskLoaded(driveIndex, loadedFilename);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
resolve({
|
|
99
|
+
success: true,
|
|
100
|
+
drive: driveNum,
|
|
101
|
+
filename: loadedFilename,
|
|
102
|
+
size: data.length,
|
|
103
|
+
message: `Disk inserted into drive ${driveNum}: ${loadedFilename}`,
|
|
104
|
+
});
|
|
105
|
+
},
|
|
106
|
+
onError: (error) => {
|
|
107
|
+
reject(new Error(`Failed to insert disk: ${error}`));
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
} catch (error) {
|
|
112
|
+
throw new Error(`Error loading disk image: ${error.message}`);
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get list of recent disks for a drive
|
|
118
|
+
*/
|
|
119
|
+
driveRecentsList: async (args) => {
|
|
120
|
+
const { driveNum = 1 } = args;
|
|
121
|
+
|
|
122
|
+
// Validate driveNum (1 or 2)
|
|
123
|
+
if (driveNum !== 1 && driveNum !== 2) {
|
|
124
|
+
throw new Error("driveNum must be 1 or 2");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Convert to 0-based index
|
|
128
|
+
const driveIndex = driveNum - 1;
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
// Get recent disks from IndexedDB
|
|
132
|
+
const recentDisks = await getRecentDisks(driveIndex);
|
|
133
|
+
|
|
134
|
+
// Return list of filenames with access times
|
|
135
|
+
const diskList = recentDisks.map(disk => ({
|
|
136
|
+
filename: disk.filename,
|
|
137
|
+
accessedAt: disk.accessedAt,
|
|
138
|
+
id: disk.id,
|
|
139
|
+
}));
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
success: true,
|
|
143
|
+
drive: driveNum,
|
|
144
|
+
count: diskList.length,
|
|
145
|
+
disks: diskList,
|
|
146
|
+
message: `Found ${diskList.length} recent disk(s) for drive ${driveNum}`,
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
} catch (error) {
|
|
150
|
+
throw new Error(`Error getting recent disks: ${error.message}`);
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Insert a recent disk by name
|
|
156
|
+
*/
|
|
157
|
+
driveInsertRecent: async (args) => {
|
|
158
|
+
const { driveNum = 1, name } = args;
|
|
159
|
+
|
|
160
|
+
if (!name) {
|
|
161
|
+
throw new Error("name parameter is required");
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Validate driveNum (1 or 2)
|
|
165
|
+
if (driveNum !== 1 && driveNum !== 2) {
|
|
166
|
+
throw new Error("driveNum must be 1 or 2");
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Convert to 0-based index
|
|
170
|
+
const driveIndex = driveNum - 1;
|
|
171
|
+
|
|
172
|
+
// Get disk manager and WASM module
|
|
173
|
+
const diskManager = window.emulator?.diskManager;
|
|
174
|
+
const wasmModule = window.emulator?.wasmModule;
|
|
175
|
+
|
|
176
|
+
if (!diskManager) {
|
|
177
|
+
throw new Error("Disk manager not available");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (!wasmModule) {
|
|
181
|
+
throw new Error("WASM module not available");
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Get drive state
|
|
185
|
+
const drive = diskManager.drives[driveIndex];
|
|
186
|
+
if (!drive) {
|
|
187
|
+
throw new Error(`Drive ${driveNum} not found`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
// Get recent disks list
|
|
192
|
+
const recentDisks = await getRecentDisks(driveIndex);
|
|
193
|
+
|
|
194
|
+
// Find the disk by filename
|
|
195
|
+
const diskEntry = recentDisks.find(d => d.filename === name);
|
|
196
|
+
if (!diskEntry) {
|
|
197
|
+
throw new Error(`Disk "${name}" not found in recent list for drive ${driveNum}`);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Load the disk data from IndexedDB
|
|
201
|
+
const diskData = await loadRecentDisk(diskEntry.id);
|
|
202
|
+
if (!diskData) {
|
|
203
|
+
throw new Error(`Failed to load disk data for "${name}"`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Load disk using the disk operations helper
|
|
207
|
+
return new Promise((resolve, reject) => {
|
|
208
|
+
loadDiskFromData({
|
|
209
|
+
wasmModule,
|
|
210
|
+
drive,
|
|
211
|
+
driveNum: driveIndex,
|
|
212
|
+
filename: diskData.filename,
|
|
213
|
+
data: diskData.data,
|
|
214
|
+
onSuccess: (loadedFilename) => {
|
|
215
|
+
// Update UI
|
|
216
|
+
diskManager.setDiskName(driveIndex, loadedFilename);
|
|
217
|
+
|
|
218
|
+
// Notify if callback exists
|
|
219
|
+
if (diskManager.onDiskLoaded) {
|
|
220
|
+
diskManager.onDiskLoaded(driveIndex, loadedFilename);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
resolve({
|
|
224
|
+
success: true,
|
|
225
|
+
drive: driveNum,
|
|
226
|
+
filename: loadedFilename,
|
|
227
|
+
size: diskData.data.length,
|
|
228
|
+
message: `Recent disk inserted into drive ${driveNum}: ${loadedFilename}`,
|
|
229
|
+
});
|
|
230
|
+
},
|
|
231
|
+
onError: (error) => {
|
|
232
|
+
reject(new Error(`Failed to insert disk: ${error}`));
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
} catch (error) {
|
|
238
|
+
throw new Error(`Error inserting recent disk: ${error.message}`);
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Load a recent disk by filename
|
|
244
|
+
*/
|
|
245
|
+
driveLoadRecent: async (args) => {
|
|
246
|
+
const { driveNum = 1, file } = args;
|
|
247
|
+
|
|
248
|
+
if (!file) {
|
|
249
|
+
throw new Error("file parameter is required");
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Validate driveNum (1 or 2)
|
|
253
|
+
if (driveNum !== 1 && driveNum !== 2) {
|
|
254
|
+
throw new Error("driveNum must be 1 or 2");
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Convert to 0-based index
|
|
258
|
+
const driveIndex = driveNum - 1;
|
|
259
|
+
|
|
260
|
+
// Get disk manager and WASM module
|
|
261
|
+
const diskManager = window.emulator?.diskManager;
|
|
262
|
+
const wasmModule = window.emulator?.wasmModule;
|
|
263
|
+
|
|
264
|
+
if (!diskManager) {
|
|
265
|
+
throw new Error("Disk manager not available");
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (!wasmModule) {
|
|
269
|
+
throw new Error("WASM module not available");
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Get drive state
|
|
273
|
+
const drive = diskManager.drives[driveIndex];
|
|
274
|
+
if (!drive) {
|
|
275
|
+
throw new Error(`Drive ${driveNum} not found`);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
// Get recent disks list
|
|
280
|
+
const recentDisks = await getRecentDisks(driveIndex);
|
|
281
|
+
|
|
282
|
+
// Find the disk by filename
|
|
283
|
+
const diskEntry = recentDisks.find(d => d.filename === file);
|
|
284
|
+
if (!diskEntry) {
|
|
285
|
+
throw new Error(`Disk "${file}" not found in recent list for drive ${driveNum}`);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Load the disk data from IndexedDB
|
|
289
|
+
const diskData = await loadRecentDisk(diskEntry.id);
|
|
290
|
+
if (!diskData) {
|
|
291
|
+
throw new Error(`Failed to load disk data for "${file}"`);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Load disk using the disk operations helper
|
|
295
|
+
return new Promise((resolve, reject) => {
|
|
296
|
+
loadDiskFromData({
|
|
297
|
+
wasmModule,
|
|
298
|
+
drive,
|
|
299
|
+
driveNum: driveIndex,
|
|
300
|
+
filename: diskData.filename,
|
|
301
|
+
data: diskData.data,
|
|
302
|
+
onSuccess: (loadedFilename) => {
|
|
303
|
+
// Update UI
|
|
304
|
+
diskManager.setDiskName(driveIndex, loadedFilename);
|
|
305
|
+
|
|
306
|
+
// Notify if callback exists
|
|
307
|
+
if (diskManager.onDiskLoaded) {
|
|
308
|
+
diskManager.onDiskLoaded(driveIndex, loadedFilename);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
resolve({
|
|
312
|
+
success: true,
|
|
313
|
+
drive: driveNum,
|
|
314
|
+
filename: loadedFilename,
|
|
315
|
+
size: diskData.data.length,
|
|
316
|
+
message: `Recent disk loaded into drive ${driveNum}: ${loadedFilename}`,
|
|
317
|
+
});
|
|
318
|
+
},
|
|
319
|
+
onError: (error) => {
|
|
320
|
+
reject(new Error(`Failed to insert disk: ${error}`));
|
|
321
|
+
},
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
} catch (error) {
|
|
326
|
+
throw new Error(`Error loading recent disk: ${error.message}`);
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Clear all recent disks for a drive
|
|
332
|
+
*/
|
|
333
|
+
drivesClearRecent: async (args) => {
|
|
334
|
+
const { driveNum = 1 } = args;
|
|
335
|
+
|
|
336
|
+
// Validate driveNum (1 or 2)
|
|
337
|
+
if (driveNum !== 1 && driveNum !== 2) {
|
|
338
|
+
throw new Error("driveNum must be 1 or 2");
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Convert to 0-based index
|
|
342
|
+
const driveIndex = driveNum - 1;
|
|
343
|
+
|
|
344
|
+
try {
|
|
345
|
+
// Clear recent disks from IndexedDB
|
|
346
|
+
await clearRecentDisks(driveIndex);
|
|
347
|
+
|
|
348
|
+
return {
|
|
349
|
+
success: true,
|
|
350
|
+
drive: driveNum,
|
|
351
|
+
message: `Cleared all recent disks for drive ${driveNum}`,
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
} catch (error) {
|
|
355
|
+
throw new Error(`Error clearing recent disks: ${error.message}`);
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Eject a disk from a drive
|
|
361
|
+
*/
|
|
362
|
+
diskDriveEject: async (args) => {
|
|
363
|
+
const { driveNum = 1 } = args;
|
|
364
|
+
|
|
365
|
+
// Validate driveNum (1 or 2)
|
|
366
|
+
if (driveNum !== 1 && driveNum !== 2) {
|
|
367
|
+
throw new Error("driveNum must be 1 or 2");
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Convert to 0-based index
|
|
371
|
+
const driveIndex = driveNum - 1;
|
|
372
|
+
|
|
373
|
+
// Get disk manager and WASM module
|
|
374
|
+
const diskManager = window.emulator?.diskManager;
|
|
375
|
+
const wasmModule = window.emulator?.wasmModule;
|
|
376
|
+
|
|
377
|
+
if (!diskManager) {
|
|
378
|
+
throw new Error("Disk manager not available");
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (!wasmModule) {
|
|
382
|
+
throw new Error("WASM module not available");
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Check if a disk is inserted
|
|
386
|
+
const isDiskInserted = wasmModule._isDiskInserted(driveIndex);
|
|
387
|
+
if (!isDiskInserted) {
|
|
388
|
+
return {
|
|
389
|
+
success: true,
|
|
390
|
+
drive: driveNum,
|
|
391
|
+
message: `No disk in drive ${driveNum}`,
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
try {
|
|
396
|
+
// Eject disk via WASM
|
|
397
|
+
wasmModule._ejectDisk(driveIndex);
|
|
398
|
+
|
|
399
|
+
// Update UI
|
|
400
|
+
diskManager.setDiskName(driveIndex, null);
|
|
401
|
+
|
|
402
|
+
// Notify if callback exists
|
|
403
|
+
if (diskManager.onDiskEjected) {
|
|
404
|
+
diskManager.onDiskEjected(driveIndex);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return {
|
|
408
|
+
success: true,
|
|
409
|
+
drive: driveNum,
|
|
410
|
+
message: `Disk ejected from drive ${driveNum}`,
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
} catch (error) {
|
|
414
|
+
throw new Error(`Error ejecting disk: ${error.message}`);
|
|
415
|
+
}
|
|
416
|
+
},
|
|
417
|
+
};
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* file-explorer-tools.js - File Explorer window tools
|
|
3
|
+
*
|
|
4
|
+
* Written by
|
|
5
|
+
* Shawn Bullock <shawn@agenticexpert.ai>
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export const fileExplorerTools = {
|
|
9
|
+
/**
|
|
10
|
+
* List files in a disk drive
|
|
11
|
+
*/
|
|
12
|
+
listDiskFiles: async (args) => {
|
|
13
|
+
const { drive = 0 } = args;
|
|
14
|
+
|
|
15
|
+
if (drive !== 0 && drive !== 1) {
|
|
16
|
+
throw new Error("drive must be 0 or 1");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const windowManager = window.emulator?.windowManager;
|
|
20
|
+
if (!windowManager) {
|
|
21
|
+
throw new Error("Window manager not available");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const feWindow = windowManager.getWindow("file-explorer-window");
|
|
25
|
+
if (!feWindow) {
|
|
26
|
+
throw new Error("File explorer window not found");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const wasmModule = window.emulator?.wasmModule;
|
|
30
|
+
if (!wasmModule) {
|
|
31
|
+
throw new Error("WASM module not available");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Check if disk is inserted
|
|
35
|
+
if (!wasmModule._isDiskInserted(drive)) {
|
|
36
|
+
return {
|
|
37
|
+
success: false,
|
|
38
|
+
drive: drive,
|
|
39
|
+
format: null,
|
|
40
|
+
files: [],
|
|
41
|
+
message: `No disk in drive ${drive + 1}`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Get disk data
|
|
46
|
+
const sizePtr = wasmModule._malloc(4);
|
|
47
|
+
const dataPtr = wasmModule._getDiskSectorData(drive, sizePtr);
|
|
48
|
+
const size = new Uint32Array(wasmModule.HEAPU8.buffer, sizePtr, 1)[0];
|
|
49
|
+
wasmModule._free(sizePtr);
|
|
50
|
+
|
|
51
|
+
if (!dataPtr || size === 0) {
|
|
52
|
+
return {
|
|
53
|
+
success: false,
|
|
54
|
+
drive: drive,
|
|
55
|
+
format: null,
|
|
56
|
+
files: [],
|
|
57
|
+
message: "Cannot read disk data",
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Detect format and read catalog
|
|
62
|
+
let format = null;
|
|
63
|
+
let files = [];
|
|
64
|
+
let volumeName = null;
|
|
65
|
+
|
|
66
|
+
if (wasmModule._isProDOSFormat(dataPtr, size)) {
|
|
67
|
+
format = "prodos";
|
|
68
|
+
wasmModule._getProDOSVolumeInfo(dataPtr, size);
|
|
69
|
+
volumeName = wasmModule.UTF8ToString(wasmModule._getProDOSVolumeName());
|
|
70
|
+
|
|
71
|
+
const count = wasmModule._getProDOSCatalog(dataPtr, size);
|
|
72
|
+
for (let i = 0; i < count; i++) {
|
|
73
|
+
const fileType = wasmModule._getProDOSEntryFileType(i);
|
|
74
|
+
const isDirectory = wasmModule._getProDOSEntryIsDirectory(i);
|
|
75
|
+
|
|
76
|
+
files.push({
|
|
77
|
+
filename: wasmModule.UTF8ToString(
|
|
78
|
+
wasmModule._getProDOSEntryFilename(i),
|
|
79
|
+
),
|
|
80
|
+
path: wasmModule.UTF8ToString(wasmModule._getProDOSEntryPath(i)),
|
|
81
|
+
type: fileType,
|
|
82
|
+
typeName: wasmModule.UTF8ToString(
|
|
83
|
+
wasmModule._getProDOSEntryFileTypeName(i),
|
|
84
|
+
),
|
|
85
|
+
isDirectory: isDirectory,
|
|
86
|
+
isLocked: wasmModule._getProDOSEntryIsLocked(i),
|
|
87
|
+
size: wasmModule._getProDOSEntryEOF(i),
|
|
88
|
+
blocks: wasmModule._getProDOSEntryBlocksUsed(i),
|
|
89
|
+
index: i,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
} else if (wasmModule._isDOS33Format(dataPtr, size)) {
|
|
93
|
+
format = "dos33";
|
|
94
|
+
|
|
95
|
+
const count = wasmModule._getDOS33Catalog(dataPtr, size);
|
|
96
|
+
for (let i = 0; i < count; i++) {
|
|
97
|
+
const fileType = wasmModule._getDOS33EntryFileType(i);
|
|
98
|
+
|
|
99
|
+
files.push({
|
|
100
|
+
filename: wasmModule.UTF8ToString(
|
|
101
|
+
wasmModule._getDOS33EntryFilename(i),
|
|
102
|
+
),
|
|
103
|
+
type: fileType,
|
|
104
|
+
typeName: wasmModule.UTF8ToString(
|
|
105
|
+
wasmModule._getDOS33EntryFileTypeName(i),
|
|
106
|
+
),
|
|
107
|
+
isLocked: wasmModule._getDOS33EntryIsLocked(i),
|
|
108
|
+
sectors: wasmModule._getDOS33EntrySectorCount(i),
|
|
109
|
+
index: i,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
success: true,
|
|
116
|
+
drive: drive,
|
|
117
|
+
format: format,
|
|
118
|
+
volumeName: volumeName,
|
|
119
|
+
fileCount: files.length,
|
|
120
|
+
files: files,
|
|
121
|
+
message: `Found ${files.length} file(s) on ${format ? format.toUpperCase() : "Unknown"} disk in drive ${drive + 1}`,
|
|
122
|
+
};
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get file content from disk
|
|
127
|
+
*/
|
|
128
|
+
getDiskFileContent: async (args) => {
|
|
129
|
+
const { drive = 0, filename, isBinary = true } = args;
|
|
130
|
+
|
|
131
|
+
if (!filename) {
|
|
132
|
+
throw new Error("filename parameter is required");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (drive !== 0 && drive !== 1) {
|
|
136
|
+
throw new Error("drive must be 0 or 1");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const wasmModule = window.emulator?.wasmModule;
|
|
140
|
+
if (!wasmModule) {
|
|
141
|
+
throw new Error("WASM module not available");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Check if disk is inserted
|
|
145
|
+
if (!wasmModule._isDiskInserted(drive)) {
|
|
146
|
+
throw new Error(`No disk in drive ${drive + 1}`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Get disk data
|
|
150
|
+
const sizePtr = wasmModule._malloc(4);
|
|
151
|
+
const dataPtr = wasmModule._getDiskSectorData(drive, sizePtr);
|
|
152
|
+
const size = new Uint32Array(wasmModule.HEAPU8.buffer, sizePtr, 1)[0];
|
|
153
|
+
wasmModule._free(sizePtr);
|
|
154
|
+
|
|
155
|
+
if (!dataPtr || size === 0) {
|
|
156
|
+
throw new Error("Cannot read disk data");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Detect format and find file
|
|
160
|
+
let format = null;
|
|
161
|
+
let fileIndex = -1;
|
|
162
|
+
let fileData = null;
|
|
163
|
+
|
|
164
|
+
if (wasmModule._isProDOSFormat(dataPtr, size)) {
|
|
165
|
+
format = "prodos";
|
|
166
|
+
const count = wasmModule._getProDOSCatalog(dataPtr, size);
|
|
167
|
+
|
|
168
|
+
// Find file by name or path
|
|
169
|
+
for (let i = 0; i < count; i++) {
|
|
170
|
+
const name = wasmModule.UTF8ToString(
|
|
171
|
+
wasmModule._getProDOSEntryFilename(i),
|
|
172
|
+
);
|
|
173
|
+
const path = wasmModule.UTF8ToString(
|
|
174
|
+
wasmModule._getProDOSEntryPath(i),
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
if (name === filename || path === filename) {
|
|
178
|
+
fileIndex = i;
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (fileIndex === -1) {
|
|
184
|
+
throw new Error(`File not found: ${filename}`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Read file
|
|
188
|
+
const bytesRead = wasmModule._readProDOSFile(dataPtr, size, fileIndex);
|
|
189
|
+
if (bytesRead === 0) {
|
|
190
|
+
throw new Error("Failed to read file");
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const bufPtr = wasmModule._getProDOSFileBuffer();
|
|
194
|
+
fileData = new Uint8Array(bytesRead);
|
|
195
|
+
fileData.set(new Uint8Array(wasmModule.HEAPU8.buffer, bufPtr, bytesRead));
|
|
196
|
+
} else if (wasmModule._isDOS33Format(dataPtr, size)) {
|
|
197
|
+
format = "dos33";
|
|
198
|
+
const count = wasmModule._getDOS33Catalog(dataPtr, size);
|
|
199
|
+
|
|
200
|
+
// Find file by name
|
|
201
|
+
for (let i = 0; i < count; i++) {
|
|
202
|
+
const name = wasmModule.UTF8ToString(
|
|
203
|
+
wasmModule._getDOS33EntryFilename(i),
|
|
204
|
+
);
|
|
205
|
+
if (name === filename) {
|
|
206
|
+
fileIndex = i;
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (fileIndex === -1) {
|
|
212
|
+
throw new Error(`File not found: ${filename}`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Read file
|
|
216
|
+
const bytesRead = wasmModule._readDOS33File(dataPtr, size, fileIndex);
|
|
217
|
+
if (bytesRead === 0) {
|
|
218
|
+
throw new Error("Failed to read file");
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const bufPtr = wasmModule._getDOS33FileBuffer();
|
|
222
|
+
fileData = new Uint8Array(bytesRead);
|
|
223
|
+
fileData.set(new Uint8Array(wasmModule.HEAPU8.buffer, bufPtr, bytesRead));
|
|
224
|
+
} else {
|
|
225
|
+
throw new Error("Unknown disk format");
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Return either base64 (binary) or plain text
|
|
229
|
+
if (isBinary) {
|
|
230
|
+
const base64 = btoa(String.fromCharCode(...fileData));
|
|
231
|
+
return {
|
|
232
|
+
success: true,
|
|
233
|
+
filename: filename,
|
|
234
|
+
drive: drive,
|
|
235
|
+
format: format,
|
|
236
|
+
size: fileData.length,
|
|
237
|
+
isBinary: true,
|
|
238
|
+
contentBase64: base64,
|
|
239
|
+
message: `Read ${fileData.length} bytes from ${filename}`,
|
|
240
|
+
};
|
|
241
|
+
} else {
|
|
242
|
+
// Decode as text (assuming ASCII/high-bit ASCII)
|
|
243
|
+
let text = "";
|
|
244
|
+
for (let i = 0; i < fileData.length; i++) {
|
|
245
|
+
const byte = fileData[i];
|
|
246
|
+
// Handle carriage return (0x0D) as newline
|
|
247
|
+
if (byte === 0x0d) {
|
|
248
|
+
text += "\n";
|
|
249
|
+
} else if (byte === 0x00) {
|
|
250
|
+
// Skip null bytes
|
|
251
|
+
continue;
|
|
252
|
+
} else {
|
|
253
|
+
// Strip high bit for high-bit ASCII
|
|
254
|
+
text += String.fromCharCode(byte & 0x7f);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
success: true,
|
|
259
|
+
filename: filename,
|
|
260
|
+
drive: drive,
|
|
261
|
+
format: format,
|
|
262
|
+
size: fileData.length,
|
|
263
|
+
isBinary: false,
|
|
264
|
+
content: text,
|
|
265
|
+
message: `Read ${fileData.length} bytes from ${filename}`,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* index.js - Agent module exports
|
|
3
|
+
*
|
|
4
|
+
* Written by
|
|
5
|
+
* Shawn Bullock <shawn@agenticexpert.ai>
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { AgentManager } from "./agent-manager.js";
|
|
9
|
+
export { executeAgentTool, getAvailableTools } from "./agent-tools.js";
|
|
10
|
+
export { windowTools } from "./window-tools.js";
|
|
11
|
+
export { basicProgramTools } from "./basic-program-tools.js";
|
|
12
|
+
export { mainTools } from "./main-tools.js";
|
|
13
|
+
export { diskTools } from "./disk-tools.js";
|