Complete workflows for creating, copying, archiving, downloading, and managing files through the File Manager API.
<iframe id="fo-demo" src="/online/webapp/file-manager" width="100%" height="650" frameborder="0" style="border:1px solid #ccc; border-radius:4px;"></iframe>
<script>window._wsConnect('fo-demo', 'foSocket');
window._foSeq = function (cmds, done) {
var s = window._ws('foSocket');
if (!s) return;
var i = 0;
var next = function () {
if (i >= cmds.length) { if (done) done(); return; }
var cmd = cmds[i++];
var args = cmd.concat([function () { setTimeout(next, 5); }]);
s.fire.apply(s, ['webapp::instance::request'].concat(args));
};
next();};
window._foFire = function () {
var s = window._ws('foSocket');
if (!s) return;
s.fire.apply(s, ['webapp::instance::request'].concat(Array.prototype.slice.call(arguments)));};
// --- Workflow 1 ---window.fo1create = function () {
var pkg = JSON.stringify({name:'demo',version:'1.0.0',main:'src/index.js',scripts:{start:'node src/index.js',test:'jest'}},null,2);
window._foSeq([
['gui:specialPaths:clear'],['gui:setHomePath','input://'],
['fs:mkdirp','input://project/src/'],['fs:mkdirp','input://project/test/'],
['fs:write','input://project/package.json',pkg],
['fs:write','input://project/src/index.js','const app = require("express")();\napp.get("/", (req, res) => res.json({ status: "ok" }));\napp.listen(3000);'],
['fs:write','input://project/src/utils.js','module.exports = {\n formatDate: (d) => d.toISOString().split("T")[0],\n sleep: (ms) => new Promise(r => setTimeout(r, ms))\n};'],
['fs:write','input://project/test/utils.test.js','const { formatDate } = require("../src/utils");\ntest("formats date", () => {\n expect(formatDate(new Date("2026-04-03"))).toBe("2026-04-03");\n});'],
['fs:write','input://project/README.md','# Demo Project\n\nA demo project for the file operations workflow.'],
['fs:write','input://project/.gitignore','node_modules/\ndist/\n.env'],
['gui:specialPaths:add',{path:'input://project/',name:'Project',icon:'breeze/icons/places/64/folder-development',bookmark:{category:'workspace',name:'Project',icon:'breeze/icons/places/64/folder-development'}}],
['fs:cwd','input://project/']
], function(){ alert('Step 1 complete! Project created with 6 files.\n\nNow click "Step 2: Archive" to pack it as ZIP.'); });};window.fo1archive = function () {
window._foFire('fs:archive','input://project/','input://project.zip','zip',function(e,result){
if(e) return alert('Error: '+(e.message||e));
window._foFire('fs:cwd','input://');
alert('Step 2 complete! Archive created: project.zip ('+(result?result.size:'?')+' bytes)\n\nNow click "Step 3: Download" to save it.');
});};window.fo1download = function () {
window._foFire('fs:download','input://project.zip','project.zip',function(e){ alert(e||'Download started!'); });};
// --- Workflow 2 ---window.fo2create = function () {
window._foSeq([
['fs:mkdirp','input://workspace/originals/'],['fs:mkdirp','input://workspace/backup/'],
['fs:mkdirp','input://workspace/sorted/images/'],['fs:mkdirp','input://workspace/sorted/docs/'],
['fs:write','input://workspace/originals/photo.txt','[image data placeholder]'],
['fs:write','input://workspace/originals/report.txt','Quarterly Report Q1 2026\n\nRevenue: $1.2M\nGrowth: 15%'],
['fs:write','input://workspace/originals/notes.txt','Meeting notes from April 3'],
['fs:write','input://workspace/originals/logo.txt','[SVG logo placeholder]'],
['gui:specialPaths:add',{path:'input://workspace/',name:'Workspace',icon:'breeze/icons/places/64/folder-blue',paths:[{rule:'originals\\/',name:'Originals'},{rule:'backup\\/',name:'Backup'},{rule:'sorted\\/',name:'Sorted'}],bookmark:{category:'workspace',name:'Workspace',icon:'breeze/icons/places/64/folder-blue'}}],
['fs:cwd','input://workspace/originals/']
], function(){ alert('Files created in originals/. Now click Step 2 to copy and organize.'); });};window.fo2sort = function () {
window._foSeq([
['fs:copy','input://workspace/originals/photo.txt','input://workspace/sorted/images/photo.txt'],
['fs:copy','input://workspace/originals/logo.txt','input://workspace/sorted/images/logo.txt'],
['fs:copy','input://workspace/originals/report.txt','input://workspace/sorted/docs/report.txt'],
['fs:copy','input://workspace/originals/notes.txt','input://workspace/sorted/docs/notes.txt'],
['fs:copy','input://workspace/originals/report.txt','input://workspace/backup/report-backup.txt'],
['fs:mv','input://workspace/sorted/docs/notes.txt','input://workspace/sorted/docs/meeting-notes-2026-04-03.txt'],
['fs:cwd','input://workspace/sorted/']
], function(){ alert('Step 2 complete!\n- Copied images to sorted/images/\n- Copied docs to sorted/docs/\n- Created backup of report\n- Renamed notes.txt to meeting-notes-2026-04-03.txt'); });};window.fo2verify = function () {
window._foFire('fs:ls','input://workspace/sorted/docs/',function(e,f){
if(f) alert('Sorted docs ('+f.length+' files):\n\n'+f.map(function(x){return x.filename.replace(/^.*\//,'')}).join('\n'));
});};
// --- Workflow 3 ---window.fo3multi = function () {
var data = JSON.stringify({items:Array.from({length:100},function(_,i){return{id:i,name:'item-'+i,value:Math.random()}})},null,2);
window._foSeq([
['fs:mkdirp','input://archive-test/src/'],
['fs:write','input://archive-test/src/app.js','console.log("hello world");\nmodule.exports = { greet: (n) => "Hello " + n };'],
['fs:write','input://archive-test/src/utils.js','exports.add = (a,b) => a+b;\nexports.mul = (a,b) => a*b;'],
['fs:write','input://archive-test/README.md','# Archive Test\n\nTesting multiple archive formats.'],
['fs:write','input://archive-test/data.json',data]
], function(){
window._foFire('fs:archive','input://archive-test/','input://output.zip','zip',function(e1,r1){
window._foFire('fs:archive','input://archive-test/','input://output.tar.gz','tar.gz',function(e2,r2){
window._foFire('fs:archive','input://archive-test/','input://output.7z','7z',function(e3,r3){
window._foFire('fs:cwd','input://');
alert('Archives created!\n\nZIP: '+(r1?r1.size:'?')+' bytes\nTAR.GZ: '+(r2?r2.size:'?')+' bytes\n7Z: '+(r3?r3.size:'?')+' bytes');
});
});
});
});};window.fo3dl = function (ext) {
window._foFire('fs:download','input://output.'+ext,'archive-test.'+ext);};
// --- Workflow 4 ---window.fo4create = function () {
window._foSeq([
['fs:mkdirp','input://batch/'],
['fs:write','input://batch/file-1.txt','Original content of file 1'],
['fs:write','input://batch/file-2.txt','Original content of file 2'],
['fs:write','input://batch/file-3.txt','Original content of file 3'],
['fs:write','input://batch/temp.tmp','Temporary data'],
['fs:cwd','input://batch/']
], function(){ alert('4 files created. Now click Step 2.'); });};window.fo4modify = function () {
var s = window._ws('foSocket');
if (!s) return;
s.fire('webapp::instance::request','fs:read','input://batch/file-1.txt',function(e,content){
s.fire('webapp::instance::request','fs:write','input://batch/file-1.txt',content+'\n\n--- Modified at '+new Date().toISOString()+' ---',function(){
s.fire('webapp::instance::request','fs:mkdirp','input://batch/backup/',function(){
s.fire('webapp::instance::request','fs:copy','input://batch/file-2.txt','input://batch/backup/file-2.txt',function(){
s.fire('webapp::instance::request','fs:mv','input://batch/file-3.txt','input://batch/file-3-renamed.txt',function(){
s.fire('webapp::instance::request','fs:remove','input://batch/temp.tmp',function(){
s.fire('webapp::instance::request','fs:refresh');
alert('Step 2 complete!\n- file-1.txt: appended timestamp\n- file-2.txt: copied to backup/\n- file-3.txt: renamed to file-3-renamed.txt\n- temp.tmp: deleted');
});
});
});
});
});
});};window.fo4read = function () {
window._foFire('fs:read','input://batch/file-1.txt',function(e,c){ alert(e?'Error: '+e:'file-1.txt content:\n\n'+c); });};window.fo4list = function () {
window._foFire('fs:ls','input://batch/',function(e,f){
if(f) alert('Files in batch/ ('+f.length+'):\n\n'+f.map(function(x){return x.filename.replace(/^.*\//,'')+' ('+x.length+' bytes)'}).join('\n'));
});};
// === Workflows 5-8: Bulk operations (with UI) ===
// --- Workflow 5: Bulk Copy with progress ---window.fo5create = function () {
window._foSeq([
['gui:specialPaths:clear'],['gui:setHomePath','input://'],
['fs:mkdirp','input://source/images/'],['fs:mkdirp','input://source/docs/'],
['fs:mkdirp','input://destination/'],
['fs:write','input://source/images/photo1.txt','[photo 1 data - 1.2MB]'],
['fs:write','input://source/images/photo2.txt','[photo 2 data - 2.4MB]'],
['fs:write','input://source/images/photo3.txt','[photo 3 data - 800KB]'],
['fs:write','input://source/docs/report.txt','Quarterly Report Q1 2026\nRevenue: $1.2M\nGrowth: 15%'],
['fs:write','input://source/docs/notes.txt','Meeting notes from April 2026'],
['fs:write','input://source/readme.txt','Source folder with 5 files in 2 subdirectories'],
['gui:specialPaths:add',{path:'input://source/',name:'Source',icon:'breeze/icons/places/64/folder-blue',bookmark:{category:'demo',name:'Source',icon:'breeze/icons/places/64/folder-blue'}}],
['gui:specialPaths:add',{path:'input://destination/',name:'Destination',icon:'breeze/icons/places/64/folder-green',bookmark:{category:'demo',name:'Destination',icon:'breeze/icons/places/64/folder-green'}}],
['fs:cwd','input://source/']
], function(){ alert('Source folder created with 5 files.\n\nClick Step 2 to bulk copy with progress UI.'); });};window.fo5bulkCopy = function () {
window._foFire('fs:bulkCopy',
['input://source/images/','input://source/docs/','input://source/readme.txt'],
'input://destination/',
{autoConfirm:true},
function(e){
if(e) return alert('Error: '+(e.message||e));
window._foFire('fs:cwd','input://destination/');
alert('Bulk copy complete! All files copied to destination/ with progress UI.');
}
);};window.fo5verify = function () {
window._foFire('fs:ls','input://destination/',function(e,f){
if(f) alert('Destination contents ('+f.length+' items):\n\n'+f.map(function(x){return (x.metadata._isDirectory?'[DIR] ':'[FILE] ')+x.filename.replace(/^.*\//,'')}).join('\n'));
});};
// --- Workflow 6: Bulk Remove with confirmation ---window.fo6create = function () {
window._foSeq([
['fs:mkdirp','input://cleanup/temp/'],['fs:mkdirp','input://cleanup/cache/'],['fs:mkdirp','input://cleanup/keep/'],
['fs:write','input://cleanup/temp/tmp1.txt','temp file 1'],
['fs:write','input://cleanup/temp/tmp2.txt','temp file 2'],
['fs:write','input://cleanup/cache/cache1.dat','cached data 1'],
['fs:write','input://cleanup/cache/cache2.dat','cached data 2'],
['fs:write','input://cleanup/keep/important.txt','DO NOT DELETE - important file'],
['fs:cwd','input://cleanup/']
], function(){ alert('Created cleanup/ with temp/, cache/, and keep/ folders.\n\nClick Step 2 to bulk delete temp/ and cache/ (keep/ stays).'); });};window.fo6bulkRemove = function () {
window._foFire('fs:bulkRemove',
['input://cleanup/temp/','input://cleanup/cache/'],
{autoConfirm:true},
function(e){
if(e) return alert('Error: '+(e.message||e));
window._foFire('fs:cwd','input://cleanup/');
alert('Bulk delete complete! temp/ and cache/ removed.\nimportant.txt in keep/ was preserved.');
}
);};window.fo6verify = function () {
window._foFire('fs:ls','input://cleanup/',function(e,f){
if(f) alert('Remaining in cleanup/ ('+f.length+' items):\n\n'+f.map(function(x){return (x.metadata._isDirectory?'[DIR] ':'[FILE] ')+x.filename.replace(/^.*\//,'')}).join('\n'));
});};
// --- Workflow 7: Bulk Archive with format picker ---window.fo7create = function () {
window._foSeq([
['fs:mkdirp','input://release/src/'],['fs:mkdirp','input://release/assets/'],
['fs:write','input://release/package.json',JSON.stringify({name:'my-app',version:'2.0.0',main:'src/app.js'},null,2)],
['fs:write','input://release/src/app.js','const express = require("express");\nconst app = express();\napp.listen(3000);'],
['fs:write','input://release/src/config.js','module.exports = { port: 3000, env: "production" };'],
['fs:write','input://release/assets/style.css','body { font-family: sans-serif; margin: 0; padding: 20px; }'],
['fs:write','input://release/assets/logo.txt','[SVG logo data]'],
['fs:write','input://release/README.md','# my-app v2.0.0\n\nProduction release.'],
['fs:cwd','input://release/']
], function(){ alert('Release folder created with 6 files.\n\nClick Step 2 to archive with format picker UI.'); });};window.fo7bulkArchiveUI = function () {
window._foFire('fs:bulkArchive',
'input://release/',
'input://release-v2.0.0.zip',
'zip',
function(e){
if(e) return alert('Error: '+(e.message||e));
window._foFire('fs:cwd','input://');
alert('Archive created via UI! Check input:// for release-v2.0.0.zip');
}
);};window.fo7bulkArchiveAuto = function () {
window._foFire('fs:bulkArchive',
'input://release/',
'input://release-v2.0.0.tar.gz',
'tar.gz',
{autoConfirm:true},
function(e){
if(e) return alert('Error: '+(e.message||e));
window._foFire('fs:cwd','input://');
alert('Archive created (auto-confirmed)! Check input:// for release-v2.0.0.tar.gz');
}
);};window.fo7download = function (ext) {
window._foFire('fs:download','input://release-v2.0.0.'+ext,'release-v2.0.0.'+ext);};
// --- Workflow 8: Full pipeline (bulk copy + bulk archive + download) ---window.fo8run = function () {
window._foSeq([
['gui:specialPaths:clear'],['gui:setHomePath','input://'],
['fs:mkdirp','input://project-v3/src/'],['fs:mkdirp','input://project-v3/test/'],
['fs:write','input://project-v3/package.json',JSON.stringify({name:'project',version:'3.0.0',scripts:{build:'webpack',test:'jest'}},null,2)],
['fs:write','input://project-v3/src/index.js','module.exports = { version: "3.0.0" };'],
['fs:write','input://project-v3/src/utils.js','exports.add = (a,b) => a + b;'],
['fs:write','input://project-v3/test/test.js','test("add", () => expect(add(1,2)).toBe(3));'],
['fs:write','input://project-v3/README.md','# Project v3.0.0'],
['fs:mkdirp','input://dist/']
], function(){
alert('Project created. Now bulk copying to dist/ ...');
window._foFire('fs:bulkCopy','input://project-v3/','input://dist/',{autoConfirm:true},function(e){
if(e) return alert('Copy error: '+(e.message||e));
alert('Copied to dist/. Now archiving ...');
window._foFire('fs:bulkArchive','input://dist/','input://project-v3.0.0.zip','zip',{autoConfirm:true},function(e){
if(e) return alert('Archive error: '+(e.message||e));
alert('Archived! Now downloading ...');
window._foFire('fs:download','input://project-v3.0.0.zip','project-v3.0.0.zip',function(e){
if(e) return alert('Download error: '+(e.message||e));
window._foFire('fs:cwd','input://');
alert('Full pipeline complete!\n\n1. Created project\n2. Bulk copied to dist/\n3. Archived as ZIP\n4. Downloaded to browser');
});
});
});
});};</script>
Create a project folder with files, package it as a ZIP archive, and trigger a browser download.
<button onclick="window.fo1create()">Step 1: Create Project</button><button onclick="window.fo1archive()">Step 2: Archive as ZIP</button><button onclick="window.fo1download()">Step 3: Download ZIP</button>
// Step 1: Create project files — each callback confirms the previous step finished
socket.fire("webapp::instance::request", "fs:mkdirp", "input://project/src/",
function (err) {
if (err) return console.error("mkdir failed:", err);
console.log("1/3 Directory created");
socket.fire("webapp::instance::request", "fs:write",
"input://project/package.json", JSON.stringify(pkg, null, 2),
function (err) {
if (err) return console.error("write failed:", err);
console.log("2/3 package.json written");
socket.fire("webapp::instance::request", "fs:write",
"input://project/src/index.js", "const app = ...",
function (err) {
if (err) return console.error("write failed:", err);
console.log("3/3 index.js written — project ready!");
// Step 2: Archive — callback fires when archive window closes
socket.fire("webapp::instance::request", "fs:archive",
"input://project/", "input://project.zip", "zip",
{ autoConfirm: true },
function (err) {
if (err) return console.error("archive failed:", err);
console.log("Archive created!");
// Step 3: Download — callback fires after browser download starts
socket.fire("webapp::instance::request", "fs:download",
"input://project.zip", "my-project.zip",
function (err) {
if (err) return console.error("download failed:", err);
console.log("Download started — workflow complete!");
}
);
}
);
}
);
}
);
}
);Key point: Every API event accepts a callback as the last argument. The callback fires when that operation completes (or fails). Chain callbacks to run steps sequentially. Each callback receives(err, result?)— checkerrto handle failures before proceeding.
Copy files between directories, rename them, and organize a folder structure.
<button onclick="window.fo2create()">Step 1: Create Source Files</button><button onclick="window.fo2sort()">Step 2: Copy, Sort, and Rename</button><button onclick="window.fo2verify()">Verify: List Sorted Docs</button>
// Copy files to sorted directories — callback confirms each copy finished
socket.fire("webapp::instance::request", "fs:copy",
"input://originals/photo.txt", "input://sorted/images/",
{ autoConfirm: true },
function (err) {
if (err) return console.error("Copy failed:", err);
console.log("photo.txt copied to sorted/images/");
// Rename — callback fires when move is done
socket.fire("webapp::instance::request", "fs:mv",
"input://sorted/docs/notes.txt",
"input://sorted/docs/meeting-notes-2026-04-03.txt",
function (err) {
if (err) return console.error("Rename failed:", err);
console.log("notes.txt renamed");
// Verify — callback returns the file listing
socket.fire("webapp::instance::request", "fs:ls",
"input://sorted/docs/",
function (err, files) {
if (err) return console.error("List failed:", err);
console.log("Sorted docs (" + files.length + " files):");
files.forEach(function (f) {
console.log(" " + f.filename, f.length + " bytes");
});
}
);
}
);
}
);Callback pattern:fs:copyandfs:removeopen a bin app window. The callback fires when the bin app emitsapi-request::instance:end(operation complete) orerror. This means the callback waits for the full copy/delete with progress UI to finish before firing.
Create the same content as ZIP, TAR.GZ, and 7Z to compare sizes.
<button onclick="window.fo3multi()">Create Archives in 3 Formats</button><button onclick="window.fo3dl('zip')">Download ZIP</button><button onclick="window.fo3dl('tar.gz')">Download TAR.GZ</button><button onclick="window.fo3dl('7z')">Download 7Z</button>
// Chain archives sequentially — each callback waits for the previous to finish
socket.fire("webapp::instance::request", "fs:archive",
"input://archive-test/", "input://output.zip", "zip",
{ autoConfirm: true },
function (err) {
if (err) return console.error("ZIP failed:", err);
console.log("ZIP created");
socket.fire("webapp::instance::request", "fs:archive",
"input://archive-test/", "input://output.tar.gz", "tar.gz",
{ autoConfirm: true },
function (err) {
if (err) return console.error("TAR.GZ failed:", err);
console.log("TAR.GZ created");
socket.fire("webapp::instance::request", "fs:archive",
"input://archive-test/", "input://output.7z", "7z",
{ autoConfirm: true },
function (err) {
if (err) return console.error("7Z failed:", err);
console.log("All 3 archives created!");
// Download the ZIP
socket.fire("webapp::instance::request", "fs:download",
"input://output.zip", "archive-test.zip",
function (err) {
console.log(err || "ZIP download started");
}
);
}
);
}
);
}
);Why chain? Each fs:archive opens a bin/archive window. Only one should run at a time — otherwise multiple archive windows compete for resources. The callback ensures the previous archive finished before starting the next.Create, read, modify, and clean up files in a scripted pipeline.
<button onclick="window.fo4create()">Step 1: Create Files</button><button onclick="window.fo4modify()">Step 2: Modify, Copy, Rename, Delete</button><button onclick="window.fo4read()">Verify: Read file-1.txt</button><button onclick="window.fo4list()">Verify: List All Files</button>
// Full pipeline with callbacks — each step waits for the previous
socket.fire("webapp::instance::request", "fs:read", "input://batch/file-1.txt",
function (err, content) {
if (err) return console.error("Read failed:", err);
console.log("Read file-1.txt (" + content.length + " chars)");
// Append to the file
socket.fire("webapp::instance::request", "fs:write",
"input://batch/file-1.txt",
content + "\n--- Modified at " + new Date().toISOString() + " ---",
function (err) {
if (err) return console.error("Write failed:", err);
console.log("file-1.txt modified");
// Create backup dir then copy
socket.fire("webapp::instance::request", "fs:mkdirp",
"input://batch/backup/",
function (err) {
if (err) return console.error("mkdir failed:", err);
socket.fire("webapp::instance::request", "fs:copy",
"input://batch/file-2.txt", "input://batch/backup/",
{ autoConfirm: true },
function (err) {
if (err) return console.error("Copy failed:", err);
console.log("file-2.txt copied to backup/");
// Rename
socket.fire("webapp::instance::request", "fs:mv",
"input://batch/file-3.txt",
"input://batch/file-3-renamed.txt",
function (err) {
if (err) return console.error("Rename failed:", err);
console.log("file-3.txt renamed");
// Delete temp file
socket.fire("webapp::instance::request", "fs:remove",
"input://batch/temp.tmp",
{ autoConfirm: true },
function (err) {
if (err) return console.error("Delete failed:", err);
console.log("temp.tmp deleted");
// Final: read binary for verification
socket.fire("webapp::instance::request",
"fs:readBinary",
"input://batch/file-1.txt",
function (err, dataUrl) {
if (err) return console.error(err);
console.log("Base64 length:", dataUrl.length);
console.log("Pipeline complete!");
}
);
}
);
}
);
}
);
}
);
}
);
}
);Avoiding callback hell: For complex workflows, consider wrappingsocket.firein a Promise helper: >``js function req(event) { var args = Array.prototype.slice.call(arguments, 1); return new Promise(function (resolve, reject) { args.push(function (err, result) { if (err) reject(err); else resolve(result); }); socket.fire.apply(socket, ["webapp::instance::request", event].concat(args)); }); } > // Then use async/await: async function pipeline() { var content = await req("fs:read", "input://batch/file-1.txt"); await req("fs:write", "input://batch/file-1.txt", content + "\n--- Modified ---"); await req("fs:mkdirp", "input://batch/backup/"); await req("fs:copy", "input://batch/file-2.txt", "input://batch/backup/", { autoConfirm: true }); await req("fs:mv", "input://batch/file-3.txt", "input://batch/file-3-renamed.txt"); await req("fs:remove", "input://batch/temp.tmp", { autoConfirm: true }); console.log("Pipeline complete!"); }
Copy entire directory trees with the built-in copy utility showing file-by-file progress.
<button onclick="window.fo5create()">Step 1: Create Source Files</button><button onclick="window.fo5bulkCopy()">Step 2: Bulk Copy to Destination</button><button onclick="window.fo5verify()">Verify: List Destination</button>
// Bulk copy multiple sources to a destination directory
// Opens bin/cp window with progress bars and conflict resolution
socket.fire("webapp::instance::request", "fs:bulkCopy",
["input://source/images/", "input://source/docs/", "input://source/readme.txt"],
"input://destination/",
{ autoConfirm: true }, // auto-resolve conflicts
function (err) {
if (err) return console.error("Bulk copy failed:", err);
console.log("All files copied with progress UI!");
}
);
// Without autoConfirm — user clicks "Start Copy" and resolves conflicts manually
socket.fire("webapp::instance::request", "fs:bulkCopy",
"input://source/",
"input://backup/",
function (err) {
console.log("User completed the copy operation");
}
);fs:bulkCopyvsfs:copy: Usefs:bulkCopywhen copying directory trees or multiple files — it recursively walks directories, shows per-file progress, and handles conflicts (overwrite/skip/merge/rename). Usefs:copyfor single file copies in scripted pipelines.
Delete multiple files and directories with a confirmation dialog and progress tracking.
<button onclick="window.fo6create()">Step 1: Create Files to Clean Up</button><button onclick="window.fo6bulkRemove()">Step 2: Bulk Delete temp/ and cache/</button><button onclick="window.fo6verify()">Verify: Check What Remains</button>
// Bulk delete with confirmation dialog
// Opens bin/rm window — user must click "Remove" to confirm
socket.fire("webapp::instance::request", "fs:bulkRemove",
["input://cleanup/temp/", "input://cleanup/cache/"],
function (err) {
if (err) return console.error("Delete failed:", err);
console.log("User confirmed and deleted");
}
);
// Skip confirmation dialog
socket.fire("webapp::instance::request", "fs:bulkRemove",
["input://old-data/", "input://temp-files/"],
{ autoConfirm: true },
function (err) {
console.log(err || "Deleted without asking");
}
);fs:bulkRemovevsfs:remove: Usefs:bulkRemovewhen deleting multiple paths or non-empty directories — it shows what will be deleted and asks for confirmation. Usefs:removefor deleting a single file or empty directory silently.
Create archives using the built-in archive utility with format selection, progress bars, and error recovery.
<button onclick="window.fo7create()">Step 1: Create Release Files</button><button onclick="window.fo7bulkArchiveUI()">Step 2a: Archive with UI (format picker)</button><button onclick="window.fo7bulkArchiveAuto()">Step 2b: Archive Auto (tar.gz, no UI)</button><button onclick="window.fo7download('zip')">Download ZIP</button><button onclick="window.fo7download('tar.gz')">Download TAR.GZ</button>
// With format picker UI — user selects format and clicks "Create"
socket.fire("webapp::instance::request", "fs:bulkArchive",
"input://release/",
"input://release.zip",
"zip",
function (err) {
if (err) return console.error("Archive failed:", err);
console.log("User created the archive");
}
);
// Auto-start without UI
socket.fire("webapp::instance::request", "fs:bulkArchive",
"input://release/",
"input://release.tar.gz",
"tar.gz",
{ autoConfirm: true },
function (err) {
console.log(err || "Archived automatically");
}
);
// Download directly (no save to filesystem)
socket.fire("webapp::instance::request", "fs:bulkArchive",
"input://release/",
null, // null = trigger download
"zip",
{ autoConfirm: true },
function (err) {
console.log(err || "Download triggered");
}
);fs:bulkArchivevsfs:archive: Usefs:bulkArchivewhen you want the user to see progress, choose a format, or when archiving many large files. Usefs:archivefor silent archiving in scripted pipelines where you control the format.
A complete build pipeline: create project → bulk copy to dist → bulk archive → download. Each step shows progress UI and waits for completion before proceeding.
<button onclick="window.fo8run()">Run Full Pipeline</button>
// Step 1: Create project files
socket.fire("webapp::instance::request", "fs:mkdirp", "input://project/src/", function () {
socket.fire("webapp::instance::request", "fs:write",
"input://project/src/app.js", "module.exports = {};", function () {
// Step 2: Bulk copy to dist/ (with progress UI)
socket.fire("webapp::instance::request", "fs:bulkCopy",
"input://project/",
"input://dist/",
{ autoConfirm: true },
function (err) {
if (err) return console.error("Copy failed:", err);
console.log("Copied to dist/");
// Step 3: Bulk archive dist/ (with progress UI)
socket.fire("webapp::instance::request", "fs:bulkArchive",
"input://dist/",
"input://project.zip",
"zip",
{ autoConfirm: true },
function (err) {
if (err) return console.error("Archive failed:", err);
console.log("Archived");
// Step 4: Download
socket.fire("webapp::instance::request", "fs:download",
"input://project.zip",
"project-release.zip",
function (err) {
console.log(err || "Pipeline complete!");
}
);
}
);
}
);
});
});Pipeline pattern: Each bulk operation opens a window, shows progress, and calls the callback when done. Chain them via callbacks to ensure sequential execution. With { autoConfirm: true }, the pipeline runs hands-free while still showing progress.These run silently — no windows, no progress bars. Best for scripted pipelines.
| Event | Args | Description |
|---|---|---|
fs:copy | src, dst, options?, callback | Copy a single file |
fs:remove | path, options?, callback | Delete a single file or empty directory |
fs:archive | sourcePath, outputPath?, format?, options?, callback | Create archive from directory |
fs:download | path, filename?, callback | Trigger browser download |
fs:readBinary | path, callback | Read file as base64 data URL |
All lightweight operations accept an optionaloptionsparameter (e.g.,{ autoConfirm: true }) for API consistency with bulk operations. Theoptionsparameter is always optional — omit it and pass the callback directly.
// Lightweight copy
socket.fire("webapp::instance::request", "fs:copy",
"input://src/data.json", "input://backup/data.json",
function (err) { console.log(err || "Copied"); }
);
// Lightweight archive (returns result with size)
socket.fire("webapp::instance::request", "fs:archive",
"input://project/", "input://project.zip", "zip",
function (err, result) {
console.log("Archive:", result.size, "bytes at", result.path);
}
);
// Archive without saving (get blob URL)
socket.fire("webapp::instance::request", "fs:archive",
"input://project/", null, "tar.gz",
function (err, result) {
// result.url is a blob: URL
}
);These open a bin app window with progress bars, error handling, and conflict resolution. Use for batch operations or when the user should see progress.
| Event | Args | Description |
|---|---|---|
fs:bulkCopy | sources, destination, options?, callback | Copy with progress UI (bin/cp) |
fs:bulkRemove | paths, options?, callback | Delete with confirmation UI (bin/rm) |
fs:bulkArchive | sources, outputPath?, format?, options?, callback | Archive with format selection UI (bin/archive) |
{ autoConfirm: true } in options to skip user dialogs:// Bulk copy with auto-overwrite
socket.fire("webapp::instance::request", "fs:bulkCopy",
["input://src/file1.txt", "input://src/file2.txt"],
"input://backup/",
{ autoConfirm: true },
function (err) { console.log(err || "Bulk copy done"); }
);
// Bulk delete with auto-confirm
socket.fire("webapp::instance::request", "fs:bulkRemove",
["input://old-dir/", "input://temp.txt"],
{ autoConfirm: true },
function (err) { console.log(err || "Bulk delete done"); }
);
// Bulk archive with auto-start
socket.fire("webapp::instance::request", "fs:bulkArchive",
"input://project/", "input://project.zip", "zip",
{ autoConfirm: true },
function (err) { console.log(err || "Bulk archive done"); }
);| Scenario | Use | Why |
|---|---|---|
| Copy a single config file | fs:copy | Fast, no window |
| Copy 50 files with conflict handling | fs:bulkCopy | Shows progress, user resolves conflicts |
| Delete a temp file in a script | fs:remove | Instant, silent |
| Delete a directory tree with confirmation | fs:bulkRemove | User sees what's being deleted |
| Create an archive in a pipeline | fs:archive | Returns result with size/url |
| Let user pick format and see progress | fs:bulkArchive | Full UI with format selector |
autoConfirm Option (bulk only)| Operation | Without autoConfirm | With autoConfirm |
|---|---|---|
fs:bulkCopy | Shows overwrite/skip/merge dialogs | Auto-overwrites, auto-merges |
fs:bulkRemove | Shows "Are you sure?" dialog | Deletes immediately |
fs:bulkArchive | Shows format selection + "Create" button | Starts immediately |
autoConfirm passes --yes to the bin apps:bin/cp --yes — sets OVERWRITE_ALL and MERGE_ALL flagsbin/rm -f --yes — skips confirmation dialogbin/archive --format=zip --yes — auto-starts after Packer loads