browserflow です(ソース: packages/cli/src/bin.ts)。
@browserflow/cli はまだnpmに公開されていないため、npx browserflow … は404になります。このページの
すべての例はワークスペース内のバイナリを前提にしています。クローンしたmonorepo内で
browserflow がどのように解決されるかは インストール を参照してください。1. コマンドマップ
16個のサブコマンドは4つのグループに分かれます。 コア| コマンド | 内容 | 主なフラグ |
|---|---|---|
init | flows/, tools/, browserflow.lock をスキャフォールド | — |
compile <flow> | YAML → FlowSpec (+digest) | --check, --json |
emit <flow> --to yaml|ts | flowを別のサーフェスへラウンドトリップ | — |
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 | — |
install | pinされたpackを取得 + 検証(browserflow.packs.lock) | — |
registry <sub> | mirror | ls | verify | — |
| コマンド | 内容 | 主なフラグ |
|---|---|---|
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/* 全体の慣例として適用されます)。
- 人間向けの進捗と診断 → stderr(
ctx.io.err): 状態行、タイムライン、警告、effect-planの要約。 - 機械可読な結果、かつ
--jsonの場合のみ → stdout(ctx.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.tsのjsonString())で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
flows/、tools/、browserflow.lock(空の {"entries": []})を作り、
さらにスターターのpure flowを flows/hello.yaml に作成します。冪等 です。既存ファイルは変更されないため、
一部だけスキャフォールド済みのプロジェクトで init を再実行しても、不足分だけが埋められます。フラグは取りません。
compile
- default: コンパイル済みFlowSpecを
out/<name>.flowspec.jsonに書き込み、 新しく解決されたtool versionがあればbrowserflow.lockを更新します。 --check: pureでCI向けです。型チェックしてdigestを出力し、何も書き込みません (out/もロック更新もありません)。pre-commit hookやCI gateに入れるならこれです。--json: 人間向けの要約ではなく、完全なFlowSpec(失敗時は構造化されたdiagnostics)をstdoutへ出力します。
compile はネットワークに一切触れません。tool resolutionは完全にローカルで、標準Packとあなたの recordings/ から作られる
in-memory registryに対して行われます。browserflow.lock に何を書き込むかは §6 を参照してください。
emit
--to は省略すると yaml がデフォルトです。
--to yaml: canonicalなYAML projection(@browserflow/yamlのemitYaml)です。 parserの完全な逆変換であり、compile → emit → compileは固定点になります。 これはlint --fixが書き戻す内容です。--to ts: typedなRawFlowリテラルをexportするTypeScript moduleです。@browserflow/coreの型を通してラウンドトリップし、compile()に再投入すると同一のFlowSpec + digestを再現します。
emit は browserflow compile <flow> へのポインター付きで失敗します。出力はstdoutへ送られます(それ自体が結果です)。
1行の進捗メモはstderrへ送られます。
run
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も出しません。 |
--yes | effect-summaryの確認をスキップします(§5 を参照)。 |
--record-journal <path> | 実行後、portableでoffline replay可能なJournal fixtureを <path> に書き込みます(下の 再現性 を参照)。 |
--sandbox e2b | この実行で、shell.run と fs.* toolsをローカルマシンではなくリモートE2B sandboxへルーティングします。BROWSERFLOW_SANDBOX 環境変数を上書きします。E2B_API_KEY が必要です。設定とポータビリティ を参照してください。 |
--json | 完全な Run objectをstdoutへ出力します(それ以外では、flowが output を生成した場合、その値だけを出力します)。 |
run.status === "failed")も結果を出力/書き込みますが、終了コードはnon-zeroです。
--dry は --input*/--sandbox を検証しますが、何も実行しません。
record
recordings/<name>.json に書き込みます。位置指定のnameと --name は入れ替え可能です
(--name が勝ちます)。
--input <slot>: Recordingがパラメータ化される非secret input slotを宣言します。繰り返し可能です。--secret <slot>: secret input slotを宣言します。そのbytesはRecordingに入りません(slot名だけが入ります)。繰り返し可能です。--json:{name, source_digest, path}をstdoutへ出力します。
replay
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なしです。 |
--verify | Hermeticに加えて、すべての手順のoutput digestが記録済みのものと一致することをassertします。不一致があれば、最初にdivergeした手順で明確に失敗します。 |
--rerun | effectを live で再発火します(実際のsecret/network/browserが必要です)。--yes がない限り、run と同じeffect-summary-and-confirm pathに従います。 |
--verify と --rerun は相互排他的です。
inspect
--step がない場合は、Journal eventの完全なtimelineとrunのoutputを表示します。
--step <id> がある場合は、その手順のlifecycle entryと記録済みoutputだけを表示します。--json は、フォーマット済みtimelineではなく
構造化形式をemitします。secretは両方のpathでマスクされます。
runs
runs last はstdoutに最新id だけ を出力し、合成しやすいように設計されています:
browserflow inspect $(browserflow runs last)。
lint
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だけではコマンドは失敗しません。
pack
init/add/publish は、現在のディレクトリにある
作業中のdraft file browserflow.pack.json を通して、別々の呼び出しにまたがって合成されます。
これはplain JSONで、シリアライズされた DraftPack です。
pack init <name>
pack init <name>
draftを開始します:
pack init @acme/pricing --version 0.1.0。browserflow.pack.json を書き込み/上書きします。
Versionは省略すると 0.0.0 がデフォルトです。pack add <path>
pack add <path>
tool contractまたはFlowSpec(JSON file)をdraftに追加します。
add は、どちらのZod schemaがJSONを受け入れるかによって種類を検出します。
ToolContract または FlowSpec です(flowには steps arrayがあります)。--body <path> はtoolのimplementation body fileを提供します
(toolを追加するときだけ意味があります)。pack publish --registry <dir>
pack publish --registry <dir>
canonical化、検証を行い、draftを
<dir> のcontent-addressed filesystem registryへ書き込みます。冪等 です。
同じ name@version をバイト単位で同一のcontentで再公開するとno-opになります(--json では alreadyPresent: true)。
同じidentityの下に 異なる bodyがある場合はhard errorであり、黙って上書きされません。pack export --registry <dir> --to <file>
pack export --registry <dir> --to <file>
公開済みPackを1つ(
--registry から解決)bundleし、air-gap transport可能な単一ファイルとして --to に書き出します。pack list [--registry <dir>]
pack list [--registry <dir>]
--registry なしでは、現在のdraftのtools/flowsを一覧します。--registry <dir> ありでは、
そのregistryに公開されているすべての name@version を一覧します。install
browserflow.packs.lock、--lock で上書き)。
各Packをlockのmanifest digestとtoolごとのcontract digestに照らして 再検証 し(実際にserveされているものからのdriftを拒否)、
ローカルregistryへ再公開します(デフォルトは .browserflow/registry、--to で上書き)。
同じlockを install した2台のマシンは、バイト単位で同一のtoolsを持つことになります。
registry
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で終了します。
serve
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 です。
mcp
inputSchema はflowのtypedな inputs から自動的に導出されます。
agentは他のMCPツールとまったく同じようにflowを呼び出します。ただしflow自体は決定論的に実行されます(run loopにmodelはありません)。
serve と同じ <flow> / --registry resolutionを使います。
schedule
croner 経由)をセットし、指定されたscheduleで固定入力
(--input / --input-file、run と同じ方法でmerge)を使ってflowを実行します。--cron は必須です。
--registry <dir> を使う場合、対象flowはregistryが解決する 最初の flowです。
より具体的に指定したい場合は、単一flowのregistryを渡すか、<flow> を直接使ってください。
各fireは run <id> <status> をstderrへlogします。errorは schedule error: … としてstderrへ送られます。
- 安全なデフォルト
compile(どのmodeでも)と、default hermetic modeまたは--verifyのreplayは、 networkに関して常にside-effect-freeであり、決して promptを出しません。pack publishとregistry mirrorはcontent digestによって冪等です。変更のないcontentで再実行するとno-opになり、 本物のconflictは報告され、黙って上書きされることはありません。- resolved tool contractがcapabilities
net、fs、browser、spawnのいずれかを宣言している場合、 手順は「outward-facing」です(packages/cli/src/effects-plan.ts)。
- 2つのロックファイルの比較
どちらも .lock で終わり、どちらもプロジェクトルートにありますが、pinするものは
完全に異なり、読み書きする コマンドも異なり、schemaも異なります。混同しないでください。
browserflow.lock | browserflow.packs.lock | |
|---|---|---|
| Pins | flowを コンパイル するときに使う個別の tool contract resolution(ref → version, contract_digest) | install 用にpinされた Pack 全体(name, version, manifest digest と各Packのtool digest) |
| Schema | { entries: [{ ref, version, contract_digest }] }(@browserflow/core の Lockfile/LockEntry) | { schema_version: "1.0", packs: [{ name, version, digest, tools: [{ ref, contract_digest }] }] }(@browserflow/registry の BrowserFlowLock) |
| Written by | browserflow init(空のskeleton)と browserflow compile <flow>(--check ではない): 新しく解決されたtool versionで自動更新されます | 今日のCLIでは何も書きません(上の install warningを参照): @browserflow/registry の lockFromRegistry() + writeLock() で生成されます |
| Read by | compile/run/lint/emit(loadAndCompile 経由)。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を取得します |
browserflow.lock entryが示すのは 「最後にコンパイルしたとき、このtool refはこのdigestに解決された」 であり、
browserflow.packs.lock entryが示すのは 「このdigestのこのPack全体を、install が取得して検証すべきである」 です。
7. インナーループ
- ワークフローとしての再現性
記録済みJournalはportableで実行可能なfixtureです(flow + inputs + journal + journalが参照するすべてのblobが一緒にbundleされます。
packages/cli/src/commands/run.ts の JournalFixture を参照してください)。--verify はすべての手順のoutputがまだ一致することをassertします。
tool upgradeが挙動を変えた場合、replayは最初にdivergeした手順で明確に失敗します。
これにより「自分のrefactorで何か変わったか?」が、1コマンドの決定論的なチェックになります。
また、live dependencyなしでflowをCIでテスト可能にします。
- 役に立つエラー
Diagnosticsは compile、lint、emit 全体で 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と、その読み方です。
設定とポータビリティ
--sandbox、BROWSERFLOW_STORE、その他のenv-level swapがどのように動作するかです。Launcher
1つのflowのfrontとしての
serve/mcp/schedule の全体像です。ロードマップ
何がship済みで、次に何が来るか(live interactive
record captureを含む)です。