CLIは入り口であり、DXは後付けではなく機能です。基本ルールは次のとおりです。 すべての概念には明らかなコマンドが1つあり、すべてのコマンドは安全に実行でき、高速で、 説明可能であること。 バイナリは browserflow です(ソース: packages/cli/src/bin.ts)。
@browserflow/cli はまだnpmに公開されていないため、npx browserflow … は404になります。このページの すべての例はワークスペース内のバイナリを前提にしています。クローンしたmonorepo内で browserflow がどのように解決されるかは インストール を参照してください。

1. コマンドマップ

16個のサブコマンドは4つのグループに分かれます。 コア
コマンド内容主なフラグ
initflows/, tools/, browserflow.lock をスキャフォールド
compile <flow>YAML → FlowSpec (+digest)--check, --json
emit <flow> --to yaml|tsflowを別のサーフェスへラウンドトリップ
run <flow>ローカルで実行
lint <flow>静的チェック + スタイル--fix
オーサリングとリプレイ
コマンド内容主なフラグ
record --name <n> --task <t> --ops <file>ブラウザーRecordingを作成
replay <run-id|journal>runを再現--verify, --rerun
inspect <run-id>記録済みrunをタイムトラベル--step <id>
runs [last]最近のrunを一覧
配布
コマンド内容主なフラグ
pack <sub>init | add | publish | export | list
installpinされたpackを取得 + 検証(browserflow.packs.lock
registry <sub>mirror | ls | verify
Launcher
コマンド内容主なフラグ
serve [flow]REST launcher (Hono) — DEV-ONLY
mcp [flow]MCP launcher (stdio)
schedule <flow> --cron <e>ローカルcron launcher
いつでも browserflow help(または browserflow --help / -h)を実行できます。これは packages/cli/src/dispatch.ts でdispatchが定義しているリテラルの HELP 文字列を出力します。
コマンドごとの --help テキストジェネレーターはまだありません。任意のサブコマンドの後に --help/-h を渡しても、コマンド固有の使い方ではなく同じトップレベルマップが出力されます。 それが存在するまでは、このページと各コマンド自身のエラーがリファレンスです。

2. 引数パースのルール

packages/cli/src/argv.ts は小さな依存なしのパーサーです。正しい呼び出しを書くうえで重要です。
  • --flag はbooleanの true--no-flag はbooleanの false です。
  • --key value--key=value はどちらも値を束縛します。ただし --key value が束縛されるのは、 key がdispatcherの既知の value-flag リスト(input, input-file, record-journal, sandbox, to, step, run-id, version, registry, from, lock, mode, packs, body, port, host, token, cron, name, task, ops, secret)に含まれる場合だけです。それ以外のフラグは常にboolean switchなので、 --json report.json--json(true)の後に位置引数 report.json が続くものとしてパースされます。 これは望む動作ではありません。そのリストにないものには --key=value を使ってください。
  • 繰り返されたvalue-flagは配列に蓄積されます(例: --input a=1 --input b=2)。
  • -x 形式の短いフラグは、=value が付いていない限りbooleanです(例: -h)。
  • -- はフラグパースを停止します。それ以降はすべて位置オペランドです。

3. 出力規律

すべてのコマンドは同じルールに従います(packages/cli/src/commands/* 全体の慣例として適用されます)。
  • 人間向けの進捗と診断 → stderrctx.io.err): 状態行、タイムライン、警告、effect-planの要約。
  • 機械可読な結果、かつ --json の場合のみ → stdoutctx.io.out): そのため browserflow compile flow.yaml --check --json | jq .digest は、stderrのノイズが混ざらないパイプラインとして合成できます。
  • シークレットは常にマスクされます--json の有無にかかわらず、すべてのストリームで同じです。 maskText()packages/cli/src/io.ts)は、書き込まれる前の任意の文字列内で、既知のすべての シークレット値を *** に置き換えます。対象はJournalタイムライン、inspect 出力、 run/replay 結果、--record-journal が書くjournal fixtureファイルです。 これはJournal自体にあるエンジン側のredactionに重ねる、多重防御です。
  • --json 出力は 決定論的にソートされたキーio.tsjsonString())でpretty-printされるため、 同じ入力で同じflowを2回実行すると、バイト単位で同一のJSONが生成されます。CIでのdiffに便利です。
いくつかのコマンドは、--json に関係なく 無条件に stdoutへ出力します。値そのものが機械的に合成可能な結果だからです。 runs last(run idなので browserflow inspect $(browserflow runs last) が動作します)、emit(emitされたソース)、 そして run/replay(flowが output を生成した場合、その値)が該当します。

4. コマンド

init

browserflow init
現在のディレクトリに flows/tools/browserflow.lock(空の {"entries": []})を作り、 さらにスターターのpure flowを flows/hello.yaml に作成します。冪等 です。既存ファイルは変更されないため、 一部だけスキャフォールド済みのプロジェクトで init を再実行しても、不足分だけが埋められます。フラグは取りません。

compile

browserflow compile <flow> [--check] [--json]
YAML → FlowSpecを、content-addressedなdigest付きで行います。
  • default: コンパイル済みFlowSpecを out/<name>.flowspec.json に書き込み、 新しく解決されたtool versionがあれば browserflow.lock を更新します。
  • --check: pureでCI向けです。型チェックしてdigestを出力し、何も書き込みませんout/ もロック更新もありません)。pre-commit hookやCI gateに入れるならこれです。
  • --json: 人間向けの要約ではなく、完全なFlowSpec(失敗時は構造化されたdiagnostics)をstdoutへ出力します。
browserflow compile flows/price.yaml --check
browserflow compile flows/price.yaml --check --json | jq .digest
compile はネットワークに一切触れません。tool resolutionは完全にローカルで、標準Packとあなたの recordings/ から作られる in-memory registryに対して行われます。browserflow.lock に何を書き込むかは §6 を参照してください。

emit

browserflow emit <flow> --to yaml|ts
コンパイル済みflowを別のサーフェスへラウンドトリップします。--to は省略すると yaml がデフォルトです。
  • --to yaml: canonicalなYAML projection(@browserflow/yamlemitYaml)です。 parserの完全な逆変換であり、compile → emit → compile は固定点になります。 これは lint --fix が書き戻す内容です。
  • --to ts: typedな RawFlow リテラルをexportするTypeScript moduleです。 @browserflow/core の型を通してラウンドトリップし、compile() に再投入すると同一のFlowSpec + digestを再現します。
flowはすでにコンパイル可能でなければなりません。コンパイルできない場合、emitbrowserflow compile <flow> へのポインター付きで失敗します。出力はstdoutへ送られます(それ自体が結果です)。 1行の進捗メモはstderrへ送られます。
browserflow emit flows/price.yaml --to yaml > flows/price.canonical.yaml
browserflow emit flows/price.yaml --to ts > flows/price.gen.ts

run

browserflow run <flow> [--input k=v]... [--input-file <path>] [--dry] [--yes]
                        [--record-journal <path>] [--sandbox e2b] [--json]
コンパイルし、RunDeps を構築し、core Riderを通してflowを実行します。
フラグ効果
--input k=v入力を1つ渡します。繰り返し可能です。値はJSONとしてパースできる場合はJSONとしてパースされ(n=3, b=true, o={"a":1})、それ以外は生の文字列として保持されます(query=headphones はquote不要です)。
--input-file <path>入力のJSONファイルです。任意の --input k=v ペアの 下に マージされます(キー衝突時はペアが勝ちます)。
--dry計画のみです。DAGを解決し、入力を検証し、どの手順が実行 されるか と、それらがどの外向きeffectを実行 するか を出力します。effectは一切実行せず、promptも出しません。
--yeseffect-summaryの確認をスキップします(§5 を参照)。
--record-journal <path>実行後、portableでoffline replay可能なJournal fixtureを <path> に書き込みます(下の 再現性 を参照)。
--sandbox e2bこの実行で、shell.runfs.* toolsをローカルマシンではなくリモートE2B sandboxへルーティングします。BROWSERFLOW_SANDBOX 環境変数を上書きします。E2B_API_KEY が必要です。設定とポータビリティ を参照してください。
--json完全な Run objectをstdoutへ出力します(それ以外では、flowが output を生成した場合、その値だけを出力します)。
browserflow run flows/price.yaml --input query=headphones --dry
browserflow run flows/price.yaml --input query=headphones
browserflow run flows/price.yaml --input-file ./fixtures/inputs.json --yes
browserflow run flows/price.yaml --input query=x --record-journal ./fix/price.journal.json
失敗したrun(run.status === "failed")も結果を出力/書き込みますが、終了コードはnon-zeroです。 --dry--input*/--sandbox を検証しますが、何も実行しません。

record

browserflow record --name <n> --task "<description>" --ops <file>
                    [--input <slot>]... [--secret <slot>]... [--json]
オーサリング時専用です。解決済みのブラウザー操作リストをcanonical化し、content-addressedな Recording として recordings/<name>.json に書き込みます。位置指定のnameと --name は入れ替え可能です (--name が勝ちます)。
Live interactive Playwright captureは 未実装 です。--ops <file> は必須です。省略すると、record は 正確にこのメッセージでthrowします: “a JSON/YAML list of resolved operations); live interactive capture is not available in this build.” --ops ファイルは、すでに解決済みの Operation object(navigate / click / fill / select / drag / upload / extractOperationSchema で検証されます)のJSONまたはYAML配列であり、ブラウザーセッションの transcript ではありません。 これは将来のinteractive recorderが背後に置かれる決定論的な境界です。ユーザー向けコピーでlive captureを約束する前に、 現在の状態をownerに確認してください。
  • --input <slot>: Recordingがパラメータ化される非secret input slotを宣言します。繰り返し可能です。
  • --secret <slot>: secret input slotを宣言します。そのbytesはRecordingに入りません(slot名だけが入ります)。繰り返し可能です。
  • --json: {name, source_digest, path} をstdoutへ出力します。
browserflow record --name search-price \
  --task "Search a product, read the first price" \
  --ops ./ops/search-price.ops.json \
  --input query

replay

browserflow replay <run-id|journal> [--verify | --rerun] [--yes] [--json]
runを再現します。対象は、ローカルstoreにすでにあるrun id、またはJournal fixtureファイル (run --record-journal が書くもの)へのpathです。replay は、引数が読み取れて .json で終わるか、 / を含むかによってfile argumentを検出し、そこから新しいin-memory storeをrehydrateします。
モード挙動
(default) hermeticすべての net/clock/random/fs-read/browser callは完全にJournalから応答されます。外部callなし、secret不要、networkなしです。
--verifyHermeticに加えて、すべての手順のoutput digestが記録済みのものと一致することをassertします。不一致があれば、最初にdivergeした手順で明確に失敗します。
--reruneffectを live で再発火します(実際のsecret/network/browserが必要です)。--yes がない限り、run と同じeffect-summary-and-confirm pathに従います。
--verify--rerun は相互排他的です。
browserflow replay ./fix/price.journal.json --verify
browserflow replay 01HXYZ... --rerun --yes

inspect

browserflow inspect <run-id> [--step <id>] [--json]
記録済みrunをタイムトラベルします。--step がない場合は、Journal eventの完全なtimelineとrunのoutputを表示します。 --step <id> がある場合は、その手順のlifecycle entryと記録済みoutputだけを表示します。--json は、フォーマット済みtimelineではなく 構造化形式をemitします。secretは両方のpathでマスクされます。
browserflow inspect $(browserflow runs last)
browserflow inspect 01HXYZ... --step notify --json

runs

browserflow runs
browserflow runs last
記録済みrun idを新しい順に一覧します。statusとstart timeはstderrへ、スクリプト用のbare id listはstdoutへ出力されます。 runs last はstdoutに最新id だけ を出力し、合成しやすいように設計されています: browserflow inspect $(browserflow runs last)

lint

browserflow lint <flow> [--fix]
compile 自身のdiagnostics(type/reference/cycle/secret-leak checks)を再利用します。 lintは2つ目の並列checkerを実行しません。さらに、安価で意見のあるstyle findingを2つ追加します。
  • non-canonical: ファイルが emit --to yaml が生成するcanonical YAML formになっていません。
  • no-output: flowが output: projectionを宣言していないため、runが結果を生成しません。
--fix はファイルをcanonical formへ書き換えます。canonicalizationは固定点なので、lintが安全に適用できる唯一の機械的fixです。 compile failureはdiagnosticsを出力して終了コード2で終了します。style findingだけではコマンドは失敗しません。
browserflow lint flows/price.yaml
browserflow lint flows/price.yaml --fix

pack

browserflow pack init <name> [--version <v>] [--json]
browserflow pack add <path> [--body <path>] [--json]
browserflow pack publish --registry <dir> [--json]
browserflow pack export --registry <dir> --to <file> [--json]
browserflow pack list [--registry <dir>] [--json]
Packs(tool contract + flowのbundle)を作成して公開します。init/add/publish は、現在のディレクトリにある 作業中のdraft file browserflow.pack.json を通して、別々の呼び出しにまたがって合成されます。 これはplain JSONで、シリアライズされた DraftPack です。
draftを開始します: pack init @acme/pricing --version 0.1.0browserflow.pack.json を書き込み/上書きします。 Versionは省略すると 0.0.0 がデフォルトです。
tool contractまたはFlowSpec(JSON file)をdraftに追加します。add は、どちらのZod schemaがJSONを受け入れるかによって種類を検出します。 ToolContract または FlowSpec です(flowには steps arrayがあります)。--body <path> はtoolのimplementation body fileを提供します (toolを追加するときだけ意味があります)。
canonical化、検証を行い、draftを <dir> のcontent-addressed filesystem registryへ書き込みます。冪等 です。 同じ name@version をバイト単位で同一のcontentで再公開するとno-opになります(--json では alreadyPresent: true)。 同じidentityの下に 異なる bodyがある場合はhard errorであり、黙って上書きされません。
公開済みPackを1つ(--registry から解決)bundleし、air-gap transport可能な単一ファイルとして --to に書き出します。
--registry なしでは、現在のdraftのtools/flowsを一覧します。--registry <dir> ありでは、 そのregistryに公開されているすべての name@version を一覧します。
browserflow pack init @acme/pricing --version 0.1.0
browserflow pack add tools/search-price.contract.json --body tools/search-price.body.json
browserflow pack publish --registry ./registry
browserflow pack export --registry ./registry --to ./pricing-0.1.0.packbundle

install

browserflow install --from <registry-dir> [--lock <file>] [--to <dir>] [--json]
registry pack lockにpinされたすべてのPackを取得して検証します(デフォルトは browserflow.packs.lock--lock で上書き)。 各Packをlockのmanifest digestとtoolごとのcontract digestに照らして 再検証 し(実際にserveされているものからのdriftを拒否)、 ローカルregistryへ再公開します(デフォルトは .browserflow/registry--to で上書き)。 同じlockを install した2台のマシンは、バイト単位で同一のtoolsを持つことになります。
browserflow install --from ./upstream-registry
browserflow install --from ./upstream-registry --lock ./ci.packs.lock --to .browserflow/registry
現在、browserflow.packs.lock書き込む browserflow サブコマンドはありません。 これは @browserflow/registrylockFromRegistry() + writeLock() を通してプログラム的に生成されます。 CLI自身のtest suite(packages/cli/src/commands/pack.spec.ts)では実行されていますが、ユーザー向けサブコマンドとして packregistry には配線されていません。今日必要な場合は、その2つのregistry-package関数に対してscriptを書くか、 pack lock コマンドが計画されているかownerに確認してください。

registry

browserflow registry mirror --from <dir> --to <dir> [--packs <pattern>]... --mode pull|push|sync [--json]
browserflow registry ls --registry <dir> [--json]
browserflow registry verify --registry <dir> [--json]
content-addressed registry上の複製とintegrityです。
  • mirror: content-addressedなので、安全で冪等、かつincrementalです。 --mode pull--from から --to へコピーし、push は逆向きにコピーします。 sync は双方向です(新しいdigestが勝ちます)。また、同じ name@version の下に2つの 異なる bodyが存在する場合は、 黙って上書きせずhard conflictを報告します。--packs <pattern> はmirror対象のPackをfilterします。繰り返し可能です。 conflictがあると、一部の作業がコピー済みであっても mirror はnon-zeroで終了します。
  • ls: registryのPackを一覧します。
  • verify: すべてのPackを再解決し(すべてのartifactとmanifestを再hash)、失敗したものを報告します。失敗したPackがあればnon-zeroで終了します。
browserflow registry mirror --from ./local --to ./hub --mode push
browserflow registry ls --registry ./hub
browserflow registry verify --registry ./hub --json

serve

browserflow serve [flow] [--registry <dir>] [--port <n>] [--host <addr>]
                   [--allow-remote] [--token <t>]
公開済みflowごとに POST /flows/:name/runs として公開するREST launcher(Hono)です (request bodyはinputs、responseは Run です。または ?mode=async 付きでは {run_id, status} を持つ 202 を返し、 GET /runs/:id でpollできます)。discovery用の GET /flows もあります。 単一の <flow> YAML path、または --registry <dir> に公開されたすべてのflowをserveします。 デフォルトは --port 8080--host 127.0.0.1 です。
serve はDEV-ONLYです。 ユーザーモデルもflowごとのACLもありません。到達できるcallerは、公開済みflowをどれでも実行できます。 デフォルトでは loopback only127.0.0.1 / ::1 / localhost)にbindし、 --allow-remote かつ token(--token <t> またはcomposition rootが読む BF_SERVE_TOKEN 環境変数)の両方を渡さない限り、 それ以外のhostでは 起動を拒否 します。tokenが設定されると、すべてのrequestは Authorization: Bearer <token> を提示する必要があります(constant timeでチェックされます)。提示しない場合は 401 です。 serve をproduction APIとしてpublic internetの前に置いては いけません。その用途にはhosted control-plane (Jinba Trailの @jinbatrail/service、またはあなた自身のwrapper)があります。--allow-remote を使っていても、 serve はrequest bodyを1 MiBに制限し、idle connectionを30秒後にreapします。これはdev-loopback toolのhardeningであり、 本物のgatewayの代替ではありません。
browserflow serve flows/price.yaml
browserflow serve --registry ./registry --port 3000
BF_SERVE_TOKEN=devtoken browserflow serve flows/price.yaml --host 0.0.0.0 --allow-remote

mcp

browserflow mcp [flow] [--registry <dir>]
stdio上のMCPサーバーです。公開済みflowごとにMCPツールになり、その inputSchema はflowのtypedな inputs から自動的に導出されます。 agentは他のMCPツールとまったく同じようにflowを呼び出します。ただしflow自体は決定論的に実行されます(run loopにmodelはありません)。 serve と同じ <flow> / --registry resolutionを使います。
browserflow mcp flows/price.yaml
browserflow mcp --registry ./registry

schedule

browserflow schedule <flow> --cron "<expr>" [--input k=v]... [--input-file <path>]
                      [--registry <dir>]
ローカルcron(croner 経由)をセットし、指定されたscheduleで固定入力 (--input / --input-filerun と同じ方法でmerge)を使ってflowを実行します。--cron は必須です。 --registry <dir> を使う場合、対象flowはregistryが解決する 最初の flowです。 より具体的に指定したい場合は、単一flowのregistryを渡すか、<flow> を直接使ってください。 各fireは run <id> <status> をstderrへlogします。errorは schedule error: … としてstderrへ送られます。
browserflow schedule flows/price.yaml --cron "0 */6 * * *" --input query=headphones

  1. 安全なデフォルト

run--yes なしの場合、これから実行する外向きeffect(network host、file write、spawn、browser use)を要約し、 最初の外向きeffectの前に一度だけ確認します。--dry とpure flow(effecting stepなし)はpromptを出しません。 replay --rerun はeffectをliveで再発火するため、同一のeffect-summary-and-confirm pathに従います。
  • compile(どのmodeでも)と、default hermetic modeまたは --verifyreplay は、 networkに関して常にside-effect-freeであり、決して promptを出しません。
  • pack publishregistry mirror はcontent digestによって冪等です。変更のないcontentで再実行するとno-opになり、 本物のconflictは報告され、黙って上書きされることはありません。
  • resolved tool contractがcapabilities netfsbrowserspawn のいずれかを宣言している場合、 手順は「outward-facing」です(packages/cli/src/effects-plan.ts)。

  1. 2つのロックファイルの比較

どちらも .lock で終わり、どちらもプロジェクトルートにありますが、pinするものは 完全に異なり、読み書きする コマンドも異なりschemaも異なります。混同しないでください。
browserflow.lockbrowserflow.packs.lock
Pinsflowを コンパイル するときに使う個別の tool contract resolution(ref → version, contract_digestinstall 用にpinされた Pack 全体(name, version, manifest digest と各Packのtool digest)
Schema{ entries: [{ ref, version, contract_digest }] }@browserflow/coreLockfile/LockEntry{ schema_version: "1.0", packs: [{ name, version, digest, tools: [{ ref, contract_digest }] }] }@browserflow/registryBrowserFlowLock
Written bybrowserflow init(空のskeleton)と browserflow compile <flow>--check ではない): 新しく解決されたtool versionで自動更新されます今日のCLIでは何も書きません(上の install warningを参照): @browserflow/registrylockFromRegistry() + writeLock() で生成されます
Read bycompile/run/lint/emitloadAndCompile 経由)。flowの use: にあるrangeは、「registryに今あるもの」ではなく、正確にpinされたversion + digestへ解決されますbrowserflow install --from <dir> [--lock <file>](デフォルトは browserflow.packs.lock)。ローカルへ再公開する前に、pinされたすべてのPackをこれに照らして再検証します
Purpose決定論的な コンパイル: CIは毎回同じflowを同じFlowSpecへコンパイルします決定論的な 配布: 同じpack lockを install した2台のマシンはバイト単位で同一のtoolsを取得します
どちらもcontent-addressedであり、どちらも「自分のマシンでは動く」問題をなくすために存在します。 ただし browserflow.lock entryが示すのは 「最後にコンパイルしたとき、このtool refはこのdigestに解決された」 であり、 browserflow.packs.lock entryが示すのは 「このdigestのこのPack全体を、install が取得して検証すべきである」 です。

7. インナーループ

1

スキャフォールド(1回だけ)

browserflow init
2

型 + digest、副作用なし

browserflow compile flows/price.yaml --check
3

計画、effectを発火しない

browserflow run flows/price.yaml --input query=headphones --dry
4

実際に実行

browserflow run flows/price.yaml --input query=headphones
5

何が起きたかを正確に見る

browserflow inspect $(browserflow runs last)

  1. ワークフローとしての再現性

1

Journalを記録する

browserflow run flows/price.yaml --input query=x --record-journal ./fix/price.journal.json
2

後で、どこでも、オフラインでreplayする

browserflow replay ./fix/price.journal.json --verify
記録済みJournalはportableで実行可能なfixtureです(flow + inputs + journal + journalが参照するすべてのblobが一緒にbundleされます。 packages/cli/src/commands/run.tsJournalFixture を参照してください)。--verify はすべての手順のoutputがまだ一致することをassertします。 tool upgradeが挙動を変えた場合、replayは最初にdivergeした手順で明確に失敗します。 これにより「自分のrefactorで何か変わったか?」が、1コマンドの決定論的なチェックになります。 また、live dependencyなしでflowをCIでテスト可能にします。

  1. 役に立つエラー

Diagnosticsは compilelintemit 全体で formatDiagnostics()packages/cli/src/diagnostics-format.ts)により一貫してrenderされる構造化データです。 compile errorは正確なYAML行を指し、rule名を示し、修正を提案します。 完全なcatalogueは 診断 を参照してください。 run/replay のruntime errorには、失敗した手順、マスク済みinputs、ジャンプ先のJournal frameが含まれます: browserflow inspect <run-id> --step <id>

診断

compile-time diagnostic codeの完全なcatalogueと、その読み方です。

設定とポータビリティ

--sandboxBROWSERFLOW_STORE、その他のenv-level swapがどのように動作するかです。

Launcher

1つのflowのfrontとしての serve/mcp/schedule の全体像です。

ロードマップ

何がship済みで、次に何が来るか(live interactive record captureを含む)です。