d1-cli — Tiendas D1 (コマンドライン) は何をしますか?
D1はコロンビア最大のハードディスカウントスーパーマーケットチェーンです。そのストアフロントは VTEX IO(アカウント d1tiendas)上で稼働しており、サイト自身の公開APIが統合インターフェースとなります。スクレイピングや管理者認証情報は一切不要です。
実行方法
cd skills/commerce/d1-cli
# 1. 最初に配送地点を特定してください — 他のすべてはこれに依存します
bun run src/cli.ts region --lat 4.6486 --lng=-74.0628
# 2. 次に検索: 価格と在庫は、全国ではなく、あなたの店舗のものになります
bun run src/cli.ts search leche entera --count 5
bun run src/cli.ts search --facets category-1/lacteos-y-huevos --sort price:asc
# 3. カートを使わずに買い物かごの価格を計算
bun run src/cli.ts quote 262:2 892:1
# 3b. または、予算に基づいて購入するものを決める — SKUではなく条件で指定
bun run src/cli.ts basket --budget 30000 arroz leche aceite huevos
# 4. 実際の買い物かごを作成し、支払いは人間に任せる
bun run src/cli.ts cart add 262 --qty 2
bun run src/cli.ts cart deliver-to
bun run src/cli.ts cart checkout # URLを表示しますが、支払いは行いません
エージェントが読み取れる出力を得るには、任意のコマンドに --json を追加してください。終了コードは契約であり、この一覧が唯一のコピーです:
| コード | 意味 | 再試行? |
|---|---|---|
0 | 成功 | — |
1 | D1が拒否、または到達不能 — 配送不可の地点、利用不可または不明なSKU、障害 | はい、改善する可能性あり |
2 | コマンドの呼び出し方に誤りがある | 再試行しても無意味 |
3 | 成功したが、結果は「なし」(substitute、basket) | 再試行しても無意味 |
これらの区別ができないエージェントは、タイプミスを永遠に再試行するか、一時的な障害で諦めてしまいます。3 が存在するのは、空のカテゴリがどちらでもないからです。
cart add はライン数を --qty に設定することに注意してください。追加するわけではありません。これは実際の動作で確認されたD1自身のセマンティクスです。
単純なD1自動化が失敗する二つのこと
在庫は地域ごとに異なります。 地域を指定せずにカタログを照会すると、D1は全国カタログから回答し、仮想的な販売者 1 に対して快適な10,000ユニットを報告します。同じSKUを実際の住所でチェックアウトシミュレーションにかけると、withoutStock が返されます。これは、配送を担当する実店舗がその商品を扱っていないためです。常に最初に d1 region を実行してください。CLIは、全国価格を表示している場合、平文で警告します。
地域の解決は、APIの中で最も推測が難しい部分の一つでもあります: 座標は geoCoordinates={lon};{lat} — セミコロン区切り、経度が先 で指定します。カンマ(VTEX自身のJSONボディで同じペアに使用される)は、CHK0119(「住所には郵便番号または地理座標が必要です」)で拒否されます。これは、パラメータが存在しないのではなく、形式が間違っているように見えるメッセージです。test/region.test.ts でこの動作を固定しているため、後方互換性を壊す変更はできません。
価格は2つの単位で届きます。 SKU 262(Leche Entera Latti 900 ml、店頭価格 COP 3,500)の場合:
| API | フィールド | 値 | 単位 |
|---|---|---|---|
| intelligent-search | Price | 3500 | ペソ(整数) |
| catalog_system | Price | 3500.0 | ペソ(整数) |
| checkout orderForm | sellingPrice | 350000 | ペソの100分の1 |
どちらも単位をラベル付けしていません。catalog.ts は境界ですべてを100分の1単位に正規化し、残りのコードが一つの単位だけを持つようにします。また、検索から得られた価格がチェックアウトから得られた価格と同一にレンダリングされることをテストでアサートしています。
実行しないこと
d1 は買い物かごを構築し、価格を計算します。支払いは行いません。d1 cart checkout は、人間が合計金額を確認し、支払いを完了するためのURLを出力します。
この境界は、一つのリスト(src/endpoints.ts)から二箇所でチェックされます: 実行時、D1Client.request は、解決されたURLがD1自身のオリジンであり、18の承認済みパスのいずれかであることを確認し、そうでなければリクエストをプロセスから送信する前に拒否します。静的分析では、test/safety.test.ts は、src/ 以下のどこかに承認されていない /api/ リテラルがあれば失敗します。エンドポイント(支払いやその他)を追加するには、それがリストに登録される必要があり、決定がデフォルトではなく明示的に行われます。
実行時の部分は重要であり、静的分析だけでは不十分であることがレビューで判明した後に追加されました: --facets '../../../..' により、すべてのソースリテラルが承認されたまま、検索エンドポイントから注文決済エンドポイントにパスが解決されていました。
正確に言うと、このファイルの以前のバージョンでは過剰に主張していました。ここにあるものは、書き込みアクセス権を持つコミッターが許可リストを編集することを防ぐことはできません。リポジトリ内のテストでは不可能です。
保存される唯一の認証情報は、ストアフロントセッショントークンです。これは、サインインしたブラウザが保持するものと同じで、所有者自身の注文とカートにスコープされ、~/.config/d1-cli/session.json にモード 0600 で書き込まれます。VTEX管理の appKey/appToken はどこでも読み取られません。サインインはワンタイムメールコードによって行われます。パスワード認証は意図的に実装されていません。
チェックアウトゲート — 常にここで終了する
すべての買い物かごは d1 cart checkout で終了し、その終了コードに従って行動してください。 形式的なものではありません。これは、カート全体を現在のD1の状態と再チェックする唯一のコマンドであり、他の方法では表面化しない方法でカートが古くなることがあります。
0 すべてのラインが配送可能 — URLは安全に引き渡せる
1 少なくとも1つのラインが配送不可能 — URLは出力されるが、カートは壊れている
2 コマンドの呼び出し方に誤りがある
これは、実際の買い物かごで以下のそれぞれが発生したために存在します:
- 一晩で品切れになったライン。
PAN ARTESANAL INTEGRALは、カートに追加されてから数時間後にno tiene inventarioになりました。カートはまだレンダリングされ、合計金額も表示されていましたが、その合計金額は、死んだラインが落ちたため、静かに6.490軽くなっていました。 - 住所変更によりすべてのラインが機能しなくなった。 9つのアイテムが
cannotBeDeliveredを返したが、それぞれはまだ有効なSLAを持ち、カートはまだ支払い可能な合計金額を表示していました。 - 配送料が12倍過小報告されていた。 ラインごとの割り当てが合計のように見えていた。
3つすべてにおいて、カートは 問題なく見えました。セッションの早い段階で取得した見積もりは、現在のカートに関する証拠にはなりません。最後の変更の前に取得したチェックアウトURLを決して引き渡さないでください — ゲートを再実行してください。
1が返された場合、--json は問題のあるラインを指定する undeliverable[] と、readyToCheckout: false を返します。それらのラインを削除または置き換えて、再度実行してください。
D1が供給できないラインの置き換え
d1 substitute 192 --limit 5 # 同じカテゴリから在庫のある製品をランク付けします
終了コード0は、提案できるものがあることを意味します。3は、提案できるものがないことを意味します — d1 basket と並んで 3 を使用する2つのコマンドのうちの1つです。
3であり1ではありません。その区別が重要です: 1 は、このCLIの他のすべての場所で 「D1が拒否した、または到達できなかった」 を意味し、再試行を促します。空のカテゴリは再試行しても空のままです。そのため、1のときに再試行するポリシーのエージェントは、永久にループします。空のリストで終了コード0は、もう一つの間違った答えです — 成功として読み取られ、実行されてしまいます。
各候補は、何が変わるのかを名前で示します: ブランド、パックサイズ、$/kg または $/L、パック価格、そして獲得または喪失したLey 2120警告。これは出力であり、スコアではありません。同じ900mlで同じラベルに対して1リットルあたり -12% は、Exceso en Azúcares が付いたものとは異なる提案であり、デルタだけがそれらを区別します。
提案は行いますが、実行はしません。 substitute は決して書き込みません — 承認のために人間が d1 cart add するものを出力します。これは単なる慎重さのためではありません。顧客は、間違った代替品よりもラインを 削除する ことを2度好み、そう明示的に述べ、そのたびに正しかったのです。候補とトレードオフを提示し、人間に決定を委ね、ラインを削除する用意をしてください。
読む価値のある3つのことを伝えます:
- 「この商品はあなたの店舗でまだ在庫があります。」 在庫は店舗ごとなので、他の場所で失敗したラインでもここでは問題ないかもしれません。何かを置き換える前に確認してください。
--lat/--lngがない場合は、推測する代わりにそのように伝えます — 店舗別の主張を行うための店舗が存在しないからです。 - 「レベル2/3に拡大しました」 — リーフカテゴリに在庫がなかったため、提案はより広い売り場からのものであり、それに応じて緩くなります。
- 「N個のみが比較されました」 — カテゴリは1ページよりも大きいです。
--countを増やしてください。これは空の結果にも出力されます。なぜなら、「ここには在庫のあるものは何もない」 という主張は、部分的なスイープに対してなされた場合、注意書きがより必要だからです。
不明確なリクエストの場合の検索
「SKU 1092を買う」ではなく「米が必要」の場合、二つの軸が重要であり、どちらもデフォルトではありません:
パック価格ではなく、単価でランク付けします。 --sort per-unit は、コロンビアの法律でD1に公開が義務付けられているPUMデータを使用します。二つのランキングは実際に異なります: ARROZ ESTÁNDAR 500 GRS は最も安い パック で $1.550、$3.100/kg ですが、同じ結果で最もお買い得なのは $2.775/kg — $5.550の袋で、パック価格トップ5にはどこにも現れません。パック価格でランク付けすると、正しく見えるが悪い答えになります。
D1の検索はこれでソートできないため、--sort per-unit は、取得したページ を並べ替えます。結果セット全体ではありません。--count(最大50)を増やして範囲を広げてください。出力は、それがサポートできない最上級を暗示するのではなく、そのように伝え、サイズを公開していないため全く比較できない結果の数を示します。
マルチパックの宣言サイズはパック全体であり、1つのアイテムではありません — 「修正」しないでください。 全1,600製品の調査では、名前が一致するマルチパックの中で、D1が判断に十分な情報を提供している46製品のうち44製品がパック合計を宣言していました。製品名から N UN を解析して乗算すると、2つを修正するために44を壊してしまいます。名前に含まれる数は、PUMが何を意味するかについての証拠ではありません。
したがって、resolvePackSize は名前ではなく説明を読み取ります。PUMを持つ全1,548製品のうち、説明にアイテムあたりのサイズと数量の両方が記載されている10製品に対して動作します: 3つが修正され(SKU 718、510、1008)、7つが既にパックを宣言していることが確認され、その他すべては宣言されたままにされます。
警告ラベルを読んでください。 製品はコロンビアのパッケージ前面警告(Exceso en Azúcares、Exceso en sodio、Exceso en grasas saturadas、Exceso en grasas trans、Contiene Edulcorantes)を warnings[] として --json で伝えます。カバレッジは部分的です(カテゴリによって約70~90%)ので、存在しないことは「安全」を意味するのではなく、「宣言されていない」を意味します。
次に、ページングの前に d1 facets <query>(カテゴリ、ブランド、サブカテゴリと件数)で絞り込みます。検索は最大50ページなので、広いクエリはそのまま歩くよりもファセットでカットする方が良いです。
コマンド
| コマンド | 機能 |
|---|---|
region --lat --lng | 配送地点の解決と記憶 |
search <query> | 製品の検索(--facets --sort --count --page --available) |
suggest <partial> · trending | オートコンプリート · コロンビアで検索されているもの |
categories · facets [query] | 部門ツリー · クエリのフィルター |
substitute <sku> | 代わりに何を買うか、そして何が変わるか(--limit --count) |
quote <sku>[:qty]... | 買い物かごの価格計算、カートは変更しない |
cart [show|add|set|clear|deliver-to|checkout] | 買い物かごの構築 |
login --email / --from-cookie · whoami · logout | アカウント |
orders · order <id> | 注文履歴(詳細は許可リストに編集されます。--raw でオプトイン) |
stores near | 地点近くのD1店舗 — 場所のみ、D1は集荷を提供していません |
テスト
bun test # 金額単位、セミコロンの落とし穴、クォート内の数量、
# 不明なSKUの空値、カートの正規化、注文の編集、
# 支払い範囲を制限するエンドポイント許可リスト
bun run lint && bun run typecheck
終了コードは契約であり、飾りではありません — 上記 実行方法 の表が単一のコピーです。
完全なエンドポイントマップ、配送ティアの動作、公開APIで得られないものについては README.md を参照してください。