Was macht d1-cli — Tiendas D1 von der Kommandozeile?
D1 ist die größte Hard-Discount-Supermarktkette Kolumbiens. Der Onlineshop läuft auf
VTEX IO (Account d1tiendas), das bedeutet, die eigene öffentliche API der Website ist die
Integrationsoberfläche – kein Scraping, keine Admin-Zugangsdaten.
Aufruf
cd skills/commerce/d1-cli
# 1. Bestimme ZUERST deinen Lieferort – alles andere hängt davon ab
bun run src/cli.ts region --lat 4.6486 --lng=-74.0628
# 2. Dann suchen: Preise und Verfügbarkeit beziehen sich jetzt auf deinen Markt, nicht auf das ganze Land
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. Warenkorb bepreisen, ohne den echten Warenkorb zu verändern
bun run src/cli.ts quote 262:2 892:1
# 3b. Oder ein Budget bestimmt den Inhalt – Suchbegriffe, nicht SKUs
bun run src/cli.ts basket --budget 30000 arroz leche aceite huevos
# 4. Oder einen echten Warenkorb zusammenstellen, dann an einen Menschen zur Zahlung übergeben
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 # gibt eine URL aus; bezahlt nicht
Hänge --json an jeden Befehl an, um eine für Agenten lesbare Ausgabe zu erhalten. Exit-Codes sind ein Vertrag, und diese Liste ist die einzige Kopie davon:
| Code | Bedeutung | Wiederholen? |
|---|---|---|
0 | Erfolg | — |
1 | D1 abgelehnt oder nicht erreichbar – nicht lieferbarer Ort, nicht verfügbarer oder unbekannter Artikel, Ausfall | ja, kann helfen |
2 | Befehl falsch aufgerufen | hilft nie |
3 | Erfolg, aber die Antwort ist „keine“ (substitute, basket) | hilft nie |
Ein Agent, der diese nicht unterscheiden kann, wiederholt entweder einen Tippfehler endlos oder gibt bei einer vorübergehenden Störung auf. 3 existiert, weil eine leere Kategorie keins von beidem ist.
Beachte, dass cart add die Zeile auf --qty setzt; es wird nicht addiert. Das ist D1s eigene Semantik, live verifiziert.
Die zwei Dinge, die naive D1-Automatisierung falsch macht
Verfügbarkeit ist regionalisiert. Wird der Katalog ohne Region abgefragt, antwortet D1 aus dem nationalen Katalog und meldet bequeme 10.000 Einheiten beim fiktiven Verkäufer 1. Wird dieselbe SKU durch eine Checkout-Simulation für eine echte Adresse geschickt, kommt sie als withoutStock zurück, weil der physische Markt, der sie versenden würde, sie nicht führt. Immer zuerst d1 region; die CLI warnt im Klartext, wann immer sie nationale Preise anzeigt.
Die Region zu bestimmen ist auch der am wenigsten erratbare Teil der API: die Koordinaten werden als geoCoordinates={lon};{lat} – Semikolon-getrennt, Längengrad zuerst übergeben. Ein Komma, wie es VTEXs eigene JSON-Objekte für dasselbe Paar verwenden, wird mit CHK0119 („Adressen müssen eine Postleitzahl oder Geokoordinaten haben“) abgelehnt – eine Meldung, die klingt, als fehle der Parameter, nicht als sei er fehlerhaft. test/region.test.ts stellt dies sicher, damit eine Bereinigung es nicht rückgängig machen kann.
Preise kommen in zwei Einheiten. Für SKU 262 (Leche Entera Latti 900 ml, COP 3.500 im Regal):
| API | Feld | Wert | Einheit |
|---|---|---|---|
| intelligent-search | Price | 3500 | ganze Pesos |
| catalog_system | Price | 3500.0 | ganze Pesos |
| checkout orderForm | sellingPrice | 350000 | Hundertstel |
Keine der Einheiten ist beschriftet. catalog.ts normalisiert an der Grenze alles auf Hundertstel, sodass der Rest des Codes genau eine Einheit hat, und ein Test stellt sicher, dass ein aus der Suche stammender Preis identisch zu einem aus dem Checkout stammenden Preis dargestellt wird.
Was es nicht tun wird
d1 stellt Warenkörbe zusammen und bepreist sie. Es bezahlt nicht. d1 cart checkout gibt die URL aus, unter der ein Mensch den Gesamtbetrag prüft und die Zahlung abschließt.
Diese Grenze wird an zwei Stellen anhand einer Liste (src/endpoints.ts) geprüft: Zur Laufzeit lehnt D1Client.request jede Anfrage ab, deren aufgelöste URL nicht D1s eigener Ursprung plus einer von 18 genehmigten Pfaden ist – noch bevor die Anfrage den Prozess verlässt; statisch schlägt test/safety.test.ts fehl, wenn irgendein /api/-Literal irgendwo in src/ nicht genehmigt ist. Das Hinzufügen eines Endpunkts – für Zahlungen oder andere Zwecke – schlägt fehl, bis er gelistet ist, sodass die Entscheidung getroffen wird, nicht versehentlich übergangen.
Der Laufzeitteil ist tragend und wurde hinzugefügt, nachdem eine Überprüfung gezeigt hatte, dass eine statische Prüfung nicht ausreicht: --facets '../../../..' löste sich aus dem Such-Endpunkt heraus und landete in der Auftragsabwicklung, während jedes Quellliteral genehmigt blieb.
Präzise formuliert, weil frühere Versionen dieser Datei übertrieben haben: Nichts hier hindert einen Autor mit Schreibzugriff daran, die Whitelist zu bearbeiten. Kein test im Repository kann das überprüfen.
Die einzigen Anmeldedaten, die gespeichert werden, sind ein Storefront-Sitzungstoken – dasselbe, das ein eingeloggter Browser hält, beschränkt auf die eigenen Bestellungen und den eigenen Warenkorb des Besitzers – geschrieben in ~/.config/d1-cli/session.json mit Modus 0600. Es wird nirgendwo ein VTEX-Admin-AppKey/AppToken gelesen. Der Login erfolgt über einen einmalig per E-Mail zugesandten Code; Passwort-Authentifizierung ist bewusst nicht implementiert.
Das Checkout-Gate – immer hiermit enden
Beende jeden Warenkorb mit d1 cart checkout und reagiere auf den Exit-Code. Nicht als Formsache: Es ist der einzige Befehl, der den gesamten Warenkorb erneut gegen D1 prüft, wie er jetzt ist, und Warenkörbe werden auf eine Weise alt, die kein anderer Befehl offenlegt.
0 jede Zeile ist lieferbar – die URL kann gefahrlos übergeben werden
1 mindestens eine Zeile KANN NICHT geliefert werden; die URL wird ausgegeben, aber der Warenkorb ist defekt
2 der Befehl wurde falsch aufgerufen
Dies existiert, weil jeder dieser Fälle bei einem echten Warenkorb aufgetreten ist:
- Ein Artikel war über Nacht ausverkauft.
PAN ARTESANAL INTEGRALhatte Stunden nach dem Hinzufügenno tiene inventario. Der Warenkorb wurde trotzdem angezeigt, hatte noch eine Gesamtsumme – eine Summe, die stillschweigend um 6.490 zu niedrig war, weil die tote Zeile aus ihr herausgefallen war. - Eine Adressänderung machte jede Zeile unzustellbar. Neun Artikel kamen als
cannotBeDeliveredzurück, während jeder noch eine gültige SLA hatte und der Warenkorb noch einen zahlbaren Gesamtbetrag anzeigte. - Die Versandkosten wurden um das 12-fache zu niedrig ausgewiesen durch eine zeilenweise Zuordnung, die wie ein Gesamtbetrag aussah.
In allen drei Fällen sah der Warenkorb gut aus. Ein früher in der Sitzung erstelltes Angebot ist kein Beleg für den aktuellen Warenkorb. Übergebe daher niemals eine Checkout-URL, die du vor der letzten Änderung erhalten hast – führe das Gate erneut aus.
Wenn der Exit-Code 1 zurückgibt, liefert --json undeliverable[] mit den betreffenden Zeilen und readyToCheckout: false. Entferne oder ersetze diese Zeilen und führe es erneut aus.
Ersetzen einer von D1 nicht lieferbaren Zeile
d1 substitute 192 --limit 5 # bewertet vorrätige Produkte aus derselben Kategorie
Exit 0 bedeutet, dass ein Vorschlag existiert; 3 bedeutet, dass es keinen gibt – einer von zwei Befehlen, die 3 verwenden, zusammen mit d1 basket.
Drei, nicht eins, und der Unterschied ist der Punkt: 1 bedeutet „D1 abgelehnt oder nicht erreichbar“ überall sonst in dieser CLI und lädt zu einem erneuten Versuch ein. Eine leere Kategorie wird durch einen erneuten Versuch niemals nicht-leer, daher würde ein Agent mit einer „Wiederhole bei 1“-Richtlinie für immer darin schleifen. Exit 0 mit einer leeren Liste ist die andere falsche Antwort – sie wird als Erfolg gelesen und darauf reagiert.
Jeder Kandidat nennt was sich ändert: Marke, Packungsgröße, $/kg oder $/L, Packungspreis und alle gewonnenen oder verlorenen Ley-2120-Warnhinweise. Das ist die Ausgabe, nicht die Bewertung. Ein -12% pro Liter bei gleichen 900 ml und gleichem Etikett ist eine andere Aussage als einer, der mit Exceso en Azúcares daherkommt, und nur die Deltas unterscheiden sie.
Schlage es vor; nimm es nicht vor. substitute schreibt nie – es gibt das d1 cart add für einen Menschen zur Freigabe aus. Dies ist keine Vorsicht um ihrer selbst willen: Der Kunde hat zweimal eine gestrichene Zeile einem falschen Ersatz vorgezogen, dies explizit gesagt und hatte beide Male recht. Präsentiere die Kandidaten und die Abwägungen; lass die Person entscheiden und sei bereit, die Zeile zu streichen.
Drei Dinge, die es dir sagt und die lesenswert sind:
- „Dies ist in deinem Markt noch auf Lager.“ Die Verfügbarkeit ist marktabhängig, daher kann eine Zeile, die woanders fehlschlug, hier in Ordnung sein. Überprüfe dies, bevor du etwas ersetzt. Ohne
--lat/--lngsagt es dies, anstatt zu raten – es gibt keinen Markt, um eine marktbezogene Aussage zu treffen. - „erweitert auf Ebene 2 von 3“ – die Blattkategorie hatte nichts auf Lager, daher stammen die Vorschläge aus einem breiteren Regalbereich und sind entsprechend ungenauer.
- „nur N wurden verglichen“ – die Kategorie ist größer als eine Seite. Erhöhe
--count. Dies wird auch beim leeren Ergebnis ausgegeben, denn „nichts hier ist auf Lager“, behauptet über eine teilweise Durchsuchung, braucht den Vorbehalt mehr, nicht weniger.
Dinge finden, wenn die Anfrage vage ist
Für „wir brauchen Reis“ statt „kauf SKU 1092“ sind zwei Achsen wichtig, und keine ist die Standardeinstellung:
Sortiere nach Stückpreis, nicht nach Packungspreis. --sort per-unit verwendet die PUM-Daten, die D1 nach kolumbianischem Recht veröffentlichen muss. Die beiden Rangfolgen stimmen tatsächlich nicht überein: ARROZ ESTÁNDAR 500 GRS ist die günstigste Packung mit 1.550 $ und kostet 3.100 $/kg, während der beste Wert in denselben Ergebnissen 2.775 $/kg beträgt – in einer 5.550 $-Tüte, die in den Top 5 nach Packungspreis überhaupt nicht vorkommt. Die Sortierung nach Packungspreis liefert eine schlechtere Antwort, während sie richtig aussieht.
Da D1s Suche nicht nach diesem Kriterium sortieren kann, sortiert --sort per-unit die Seite, die du abgerufen hast, nicht die gesamte Ergebnismenge. Erhöhe --count (max. 50), um sie zu verbreitern. Die Ausgabe sagt dies, anstatt eine Übertreibung zu implizieren, die sie nicht stützen kann, und nennt, wie viele Ergebnisse keine Größe angeben und daher überhaupt nicht verglichen werden können.
Eine Mehrfachpackung deklariert die Größe der Packung, nicht eines einzelnen Artikels – „korrigiere“ dies nicht. Eine Erhebung aller 1.600 Produkte ergab, dass bei den namensmäßig vergleichbaren Mehrfachpackungen 44 der 46, bei denen D1 genug sagt, um zu entscheiden, die Packungsgesamtmenge angeben. Das Parsen von N UN aus einem Produktnamen und Multiplizieren würde diese 44 korrumpieren, um 2 zu korrigieren. Die Anzahl in einem Namen ist kein Beleg dafür, was der PUM bedeutet.
resolvePackSize liest daher Beschreibungen statt Namen. Von allen 1.548 Produkten mit einem PUM wird bei den 10 eingegriffen, deren Beschreibung sowohl eine Einzelgröße als auch eine Anzahl angibt: Drei werden korrigiert (SKU 718, 510, 1008), sieben werden bestätigt, dass sie bereits die Packung angeben, und alles andere bleibt wie deklariert.
Lies die Warnhinweise. Produkte tragen Kolumbiens Front-of-Pack-Warnungen (Exceso en Azúcares, Exceso en sodio, Exceso en grasas saturadas, Exceso en grasas trans, Contiene Edulcorantes) in warnings[] unter --json. Die Abdeckung ist partiell – etwa 70–90 % je nach Kategorie – daher bedeutet Abwesenheit „nicht deklariert“, niemals „sicher“.
Verfeinere dann mit d1 facets <query> (Kategorie, Marke, Unterkategorie mit Anzahl), bevor du Seiten durchblätterst: Die Suche ist auf 50 Seiten begrenzt, daher ist eine breite Abfrage besser durch Facetten eingegrenzt als durch Durchblättern.
Befehle
| Befehl | Funktion |
|---|---|
region --lat --lng | Lieferort bestimmen und speichern |
search <query> | Produkte finden (--facets --sort --count --page --available) |
suggest <teil> · trending | Autovervollständigung · wonach Kolumbien sucht |
categories · facets [query] | Abteilungsbaum · Filter für eine Abfrage |
substitute <sku> | Was stattdessen kaufen, und was sich ändert (--limit --count) |
quote <sku>[:anz]... | Warenkorb bepreisen, keine Veränderung |
cart [show|add|set|clear|deliver-to|checkout] | Warenkorb zusammenstellen |
login --email / --from-cookie · whoami · logout | Konto |
orders · order <id> | Bestellhistorie (Details sind auf eine Whitelist reduziert; --raw aktiviert die volle Ausgabe) |
stores near | D1-Filialen in der Nähe eines Ortes – nur Standorte, D1 bietet keine Abholung an |
Tests
bun test # Geldeinheiten, das Semikolon-Spezifikum, Menge-im-Angebot,
# Unbekannte-SKU-Vakuum, Warenkorb-Normalisierung, Bestellungs-Redaktion und
# die Endpunkt-Whitelist, die die Zahlungsoberfläche begrenzt
bun run lint && bun run typecheck
Exit-Codes sind ein Vertrag, keine Dekoration – die Tabelle unter Aufruf oben ist die einzige Kopie davon.
Siehe README.md für die vollständige Endpunktkarte, das Versandstufenverhalten und
was die öffentliche API dir nicht geben wird.