課題 i260901-06 の計画である。csc で aichat.exe を作り、node 版 (src/client/chat.mjs)はそのまま残して両方を保守する。
始まりはコマンドラインの長さである。node 版は node ...\src\client\chat.mjs と書かせる。この形はパスの区切りで 2 回落ちた。Bash に渡ると \ がエスケープとして消え、Cannot find module になる。
| 観点 | いまの node 版 | exe にしたら |
|---|---|---|
| 起動 | node ...\chat.mjs と書く。パスの区切りで 2 回落ちた |
aichat だけ。パスを書かせない |
| Node への依存 | 要る (v22.5 以降。node:sqlite のため) |
要らない。ただし CLI は DB を触らないので、そもそも sqlite は不要 |
| 保守 | 1 本 | 2 本になる。オプションを増やすたび両方を直す |
2 本を保守すると、必ず食い違う。検討を始めた日にも usage() と USAGE の一覧が食い違っていた。食い違いを検出する手立てを、作る前に決める。
| # | 論点 | 中身 |
|---|---|---|
| 1 | どこまで作るか | 全コマンドか、待受けとよく使う分 (wait say recent)だけか |
| 2 | 食い違いをどう防ぐか | 同じ振る舞いのテストを両方に当てる。オプションの表を 2 本の共通の元にできるか |
| 3 | 置き場 | 納品するものは src/。ビルドは tools/20_build/。exe の置き場も決める |
| 4 | ビルドの前提 | csc はどこにあるか。.NET SDK か、Windows 同梱のものか。まず実測する |
| 5 | exe を Git に入れるか | ビルド済みを配るのか、各自でビルドするのか |
| 6 | どちらを正とするか | 資料に書くコマンドはどちらにするか。両方書くと読み手が迷う |
| 項目 | 中身 |
|---|---|
| どこまで作るか | 全コマンド。「一部だけ」だと、どちらを使うか毎回考えることになる |
| 食い違いの防ぎ方 | src/client/options.mjs を唯一の出どころにし、JSON を埋め込む |
| ビルド | csc 単ファイル。SDK が要らない |
| 名前と置き場 | aichat.exe を root に置く。Git 管理外 (作り直せる) |
| 既定値 | 接続先と ID は持たない。node 版と同じ。黙って本番に書く事故を防ぐ |
| ソースの置き場 | src/cli-cs/。他プロジェクトが使うものなので納品側 |
| node 版の呼び方 | aichat-node.cmd を root に置き、PATH から呼べるようにした |
node 版にも「パスを書かせない」利点を持たせた。aichat-node.cmd の中身は node 版を呼ぶだけである。
おかげでプロセスを数える式が 1 つで済む。どちらで張っても、コマンドラインに aichat が残る。
2026-09-02 に作った。8 ファイルと、ビルド・書き出し・テストである。
aichat.exe root。57.5 KB。Git 管理外
src/cli-cs/Program.cs 入口・使い方の表示
src/cli-cs/Commands.cs 各コマンド
src/cli-cs/Args.cs 引数の解析
src/cli-cs/Client.cs HTTP (繋ぎ直し・503・終了コード 3)
src/cli-cs/Json.cs 小さな JSON の読み書き
src/cli-cs/Definition.cs 埋め込んだ定義を読む
src/cli-cs/WaitLog.cs 待受けの記録
src/cli-cs/Waiters.cs 走っている待受けを数える
tools/20_build/export-options.mjs options.mjs から JSON を書き出す
aichat-node.cmd node 版を呼ぶだけの包み。root。PATH から呼べる
tools/20_build/build-aichat.ps1 / .cmd ビルド
tools/40_test/measure-cli-startup.ps1 起動の速さを測る (20 回 × 2 巡)
tests/cli-cs.test.mjs node 版と出力が揃うかを見る (11 件)
exe を作り直すときは待受けを止めない。走っている待受けが aichat.exe を掴んでおり、そこには他プロジェクトの分も混ざる。build-aichat.ps1 は掴まれているときだけ tmp/ へ退避する。
定義は 1 か所にしかない。src/client/options.mjs から JSON を書き出し、ビルド時に exe へ埋め込む (csc /resource:)。手で写していないので、オプションを増やしても 2 本がずれない。
埋め込んだ JSON には schema を持たせた。形が変わったら C# 側が起動時に気づいて止まる。
そのうえで tests/cli-cs.test.mjs が両方を実際に動かして出力を比べる。時刻や番号は揺れるので形だけを見る。
| 見ていること | 中身 |
|---|---|
--help | コマンドとオプションの並びが一字一句同じか |
join | 1 行目の形。参加者の一覧は増減するので数は見ない |
| 相互に読めるか | C# 版で say したものを node 版の recent で読む |
| 廃止したオプション | 両方が同じ文で、終了コード 2 で止まるか |
| 接続先なし | 両方が終了コード 2 で止まるか |
| 定義の出どころ | 書き出した JSON と options.mjs が一致するか |
1 度だけ食い違いが出て、テストが見つけた。説明を始める列が node 版 40・C# 版 37 でずれていた。中身は同じで表示だけが違う。
目で読んで気づける差ではない。見つけたいのはまさにこの類である。
20 回 × 2 巡 (順序を入れ替え)、ウォームアップ 3 回を捨てて最小値で比べた。測るのは tools/40_test/measure-cli-startup.cmd。
Node 自体の起動の方が、C# (.NET Framework)より速かった。CLR の初期化は V8 より重い。C# が勝っているのは、node が options.mjs などを読み込む分だけである。その差も、書き換える理由になる大きさではない。
はじめの測定は粗かった。5 回の平均で、出力を | Out-Null に流していた。パイプラインの処理が乗り、プロセスの起動時間が埋もれる。ウォームアップも捨てていない。
数字は結果として近かったが、根拠としては弱かった。測り方を決めてから測り直した。
残る利点は「パスを書かせない」ことだけである。それでも価値はある。パスの区切りで 2 回落ちたのは、コマンドラインが長いことが原因だった。aichat と打てるなら、その手の事故は起きない。
| 観点 | node 版 | C# 版 |
|---|---|---|
| 書き方 | aichat-node wait :id: -p 8787 | aichat wait :id: -p 8787 |
| Node への依存 | 要る | 要らない。ただしサーバーは Node で動くので、同じ PC では意味が薄い |
| 大きさ | — | 42 KB。SDK も実行環境も要らない |
| 起動の速さ | 差はあるが、書き換える理由になる大きさではない | |