Use Case: File Operations Workflows

Complete workflows for creating, copying, archiving, downloading, and managing files through the File Manager API.

Live Demo

<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>


Workflow 1: Create Project, Archive, and Download

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>

Code

// 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&#x3F;) — check err to handle failures before proceeding.

Workflow 2: Copy, Rename, and Organize Files

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>

Code

// 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:copy and fs:remove open a bin app window. The callback fires when the bin app emits api-request::instance:end (operation complete) or error. This means the callback waits for the full copy/delete with progress UI to finish before firing.

Workflow 3: Archive in Multiple Formats and Compare

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>

Code

// 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.

Workflow 4: Batch File Operations

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>

Code

// 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 wrapping socket.fire in a Promise helper: >
`js
function req(event) &#x7B;
    var args = Array.prototype.slice.call(arguments, 1);
    return new Promise(function (resolve, reject) &#x7B;
        args.push(function (err, result) &#x7B;
            if (err) reject(err); else resolve(result);
        &#x7D;);
        socket.fire.apply(socket,
            [&#x22;webapp::instance::request&#x22;, event].concat(args));
    &#x7D;);
&#x7D;
&#x3E;
// Then use async/await:
async function pipeline() &#x7B;
    var content = await req(&#x22;fs:read&#x22;, &#x22;input://batch/file-1.txt&#x22;);
    await req(&#x22;fs:write&#x22;, &#x22;input://batch/file-1.txt&#x22;, content + &#x22;\n--- Modified ---&#x22;);
    await req(&#x22;fs:mkdirp&#x22;, &#x22;input://batch/backup/&#x22;);
    await req(&#x22;fs:copy&#x22;, &#x22;input://batch/file-2.txt&#x22;, &#x22;input://batch/backup/&#x22;, &#x7B; autoConfirm: true &#x7D;);
    await req(&#x22;fs:mv&#x22;, &#x22;input://batch/file-3.txt&#x22;, &#x22;input://batch/file-3-renamed.txt&#x22;);
    await req(&#x22;fs:remove&#x22;, &#x22;input://batch/temp.tmp&#x22;, &#x7B; autoConfirm: true &#x7D;);
    console.log(&#x22;Pipeline complete!&#x22;);
&#x7D;
`

Workflow 5: Bulk Copy with Progress UI

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>

Code

// 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:bulkCopy vs fs:copy: Use fs:bulkCopy when copying directory trees or multiple files — it recursively walks directories, shows per-file progress, and handles conflicts (overwrite/skip/merge/rename). Use fs:copy for single file copies in scripted pipelines.

Workflow 6: Bulk Remove with Confirmation UI

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>

Code

// 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:bulkRemove vs fs:remove: Use fs:bulkRemove when deleting multiple paths or non-empty directories — it shows what will be deleted and asks for confirmation. Use fs:remove for deleting a single file or empty directory silently.

Workflow 7: Bulk Archive with Format Picker

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>

Code

// 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:bulkArchive vs fs:archive: Use fs:bulkArchive when you want the user to see progress, choose a format, or when archiving many large files. Use fs:archive for silent archiving in scripted pipelines where you control the format.

Workflow 8: Full Pipeline (Bulk Copy + Bulk Archive + Download)

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>

Code

// 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 &#x7B; autoConfirm: true &#x7D;, the pipeline runs hands-free while still showing progress.

API Events Reference

Lightweight Operations (no UI)

These run silently — no windows, no progress bars. Best for scripted pipelines.

Event Args Description
fs:copy src, dst, options&#x3F;, callback Copy a single file
fs:remove path, options&#x3F;, callback Delete a single file or empty directory
fs:archive sourcePath, outputPath&#x3F;, format&#x3F;, options&#x3F;, callback Create archive from directory
fs:download path, filename&#x3F;, callback Trigger browser download
fs:readBinary path, callback Read file as base64 data URL

All lightweight operations accept an optional options parameter (e.g., &#x7B; autoConfirm: true &#x7D;) for API consistency with bulk operations. The options parameter 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
    }
);

Bulk Operations (with UI)

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&#x3F;, callback Copy with progress UI (bin/cp)
fs:bulkRemove paths, options&#x3F;, callback Delete with confirmation UI (bin/rm)
fs:bulkArchive sources, outputPath&#x3F;, format&#x3F;, options&#x3F;, callback Archive with format selection UI (bin/archive)

All bulk operations accept &#x7B; autoConfirm: true &#x7D; 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"); }
);

When to Use Which

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

The 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

Under the hood, autoConfirm passes --yes to the bin apps:

sgapps.io