Skip to Content
Excel Storage

Excel Storage

ExcelStorage lets you read profile data from Excel files and write automation output back to spreadsheets. Call flow.createExcelStorage() at the caller/runner level — not inside the flow class.

Setup

createExcelStorage() is called where you instantiate your flow (e.g. main.dev.ts):

import { AntidetectProvider } from '@hira-core/sdk' import { MyFlow } from './flow' async function bootstrap() { const flow = new MyFlow(AntidetectProvider.GPM) // ── Setup Excel Storage at the caller level ── const storage = flow.createExcelStorage({ inputFile: { filePath: './profiles.xlsx', // relative to callerDir sheetIndex: 0, profileNameColumn: 'A', columns: { 'B': 'email', 'C': 'password', }, }, outputFile: { filePath: './profiles.xlsx', // same file, different columns sheetIndex: 0, profileNameColumn: 'A', columns: { 'D': 'loginStatus', 'E': 'pageTitle', }, }, }, __dirname) // resolve relative paths from this directory // ── Read profiles from Excel ── const profileSettings = await storage.parseProfileSettings() console.log(`📊 Loaded ${profileSettings.length} profile(s) from Excel`) // ── Create run params ── const params = flow.createRunParams({ antidetect: { profileSettings }, execution: { concurrency: 3 }, globalInput: { targetUrl: 'https://example.com' }, window: { width: 1280, height: 720, scale: 0.6 }, }) await flow.run(params) } bootstrap()

[!NOTE] createExcelStorage() auto-attaches to FlowLogger — so writeOutput() and writeProfileInput() from BrowserUtils will also write to Excel automatically.

[!TIP] When outputFile is configured, SDK also preloads Excel output into context.output before beforeBrowserOpened(). This lets flows skip completed profiles without opening Chrome.

[!IMPORTANT] Call createExcelStorage() at the runner level (like main.dev.ts), not inside the flow class. The flow class only needs script().


parseProfileSettings()

parseProfileSettings(): Promise<IProfileSetting[]>

Read Excel input file and return an array of profile settings. Each row becomes one profile. Row 1 is treated as header and skipped.

Example 1 — Read profiles from Excel

const profiles = await storage.parseProfileSettings() // Returns: // [ // { profileName: 'Profile 1', data: { email: 'a@test.com', password: '123' } }, // { profileName: 'Profile 2', data: { email: 'b@test.com', password: '456' } }, // ]

Example 2 — Log loaded profile count

const profiles = await storage.parseProfileSettings() console.log(`Loaded ${profiles.length} profiles from Excel`)

Excel file format

A (Profile Name)B (email)C (password)D (loginStatus)E (pageTitle)
Profile 1a@test.comsecret
Profile 2b@test.compass123

writeOutputRow(profileName, key, value)

writeOutputRow( profileName: string, key: TOutputKeys, // type-safe from config value: ProfileOutputValue // string | number | boolean | object ): Promise<void>

Write an output value to the Excel output file for a specific profile. Finds the row by profileName, creates a new row if not found.

Example 1 — Write login result

await storage.writeOutputRow('Profile 1', 'loginStatus', 'Success') await storage.writeOutputRow('Profile 1', 'pageTitle', 'Dashboard')

Example 2 — Manual write in runner

// Usually you don't need this — writeOutput() in BrowserUtils // auto-writes to Excel. But you can do it manually: for (const profile of profileSettings) { const prev = await storage.readOutputForProfile(profile.profileName) if (!prev.loginStatus) { await storage.writeOutputRow(profile.profileName, 'loginStatus', 'Pending') } }

writeProfileInputRow(profileName, key, value)

writeProfileInputRow( profileName: string, key: TProfileInputKeys, value: string | number | boolean ): Promise<void>

Write back to the input Excel file — useful for updating tokens, cookies, etc.

Example 1 — Save updated token

await storage.writeProfileInputRow('Profile 1', 'authToken', 'new-jwt-token')

readOutputForProfile(profileName)

readOutputForProfile( profileName: string ): Promise<Record<TOutputKeys, ProfileOutputValue | null>>

Read all output values for a specific profile from the output Excel file.

Example 1 — Check previous run result

const previous = await storage.readOutputForProfile('Profile 1') if (previous.loginStatus === 'Success') { console.log('Already completed, skipping...') }

Example 2 — Resume from previous state

for (const profile of profileSettings) { const prev = await storage.readOutputForProfile(profile.profileName) if (prev.loginStatus === 'OK') { console.log(`Skipping ${profile.profileName} — already done`) } }

Initial output hydration before browser opens

When createExcelStorage() includes an outputFile, the SDK preloads output values from Excel into context.output before beforeBrowserOpened() runs.

This means you can skip a completed profile without opening Chrome:

protected beforeBrowserOpened(context) { if (context.output.loginStatus === 'Success') { context.logger.success('Already completed — skip browser open') return false } return true }

Excel example:

A (Profile Name)D (loginStatus)E (pageTitle)
Profile 1SuccessDashboard
Profile 2

For Profile 1, context.output.loginStatus is available immediately in beforeBrowserOpened().

Precedence

If the server/agent also passes existingOutputEntries, those values override Excel values for the same keys:

null defaults → Excel output → existingOutputEntries

This keeps production server output slots as the source of truth, while still supporting local Excel-driven runs.


Configuration Reference

IExcelFileConfig

PropertyTypeDefaultDescription
filePathstring—Path to Excel file (absolute or relative to callerDir)
sheetIndexnumber0Sheet index (0-based)
profileNameColumnstring"A"Column letter containing profile names
columnsRecord<string, TKeys>—Column letter → key mapping

Same file for input and output

const storage = flow.createExcelStorage({ inputFile: { filePath: './data.xlsx', profileNameColumn: 'A', columns: { 'B': 'email', 'C': 'password' }, }, outputFile: { filePath: './data.xlsx', // same file! profileNameColumn: 'A', columns: { 'D': 'status', 'E': 'result' }, // different columns }, }, __dirname)

How auto-sync works

When you call writeOutput() or writeProfileInput() inside beforeBrowserOpened() or script():

context.browserUtils.writeOutput('status', 'OK') ↓ 1. Updates context.output in memory (real-time) 2. Sends output event to Hira Agent/UI 3. Auto-writes to Excel (if ExcelStorage is set up)

context.output is shared across lifecycle hooks for the current profile:

protected async beforeBrowserOpened(context) { await context.browserUtils.writeOutput('status', 'A') return true } async script(context) { context.logger.info(String(context.output.status)) // "A" await context.browserUtils.writeOutput('status', 'B') } protected afterBrowserClosed(context) { context.logger.info(String(context.output.status)) // "B" return true }

You don’t need to manually call storage.writeOutputRow() — it happens automatically.

Last updated on