Sluzba byla v katalogu bez pristupovych udaju a s appId 'ai-text', tedy
mirila na aplikaci, ktera neexistuje. Konektor nemel co vyplnit a krok nemel
co vykonat.
- sluzba stoji na api.openai.com/v1 stejne jako OpenAI, overeni GET /models
- model se bere z konektoru (ctx.config.model), ne z kazdeho kroku zvlast
- skripty ai-text.classify, ai-text.extract, ai-text.generate
Proc to neni jedna sluzba s OpenAI: tam se posila volny dotaz a vraci volny
text. Tady je uloha pevna a vystup taky - category a confidence, objekt
s popsanymi poli, text s poctem slov. Na to jde ve strome navazat podminkou,
na volny text ne.
Zjisteno u toho a zapsane do 21-realne-sluzby.md: proxy vraci na neznamou
aplikaci 200 s prazdnym telem, takze overeni konektoru u sluzby bez
verifyPath projde i tam, kde nic nebezi. Zatim neopraveno.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Katalog srovnany s tim, co opravdu bezi na services.csbot.cz/apps:
trinact sluzeb dostalo pristupove udaje a levne cteci overeni, opravena
appId, ktera nikam nevedla (ppl, microsoft365, transcription), a GA4,
Search Console, Google Ads i Sklik ted stoji na aplikaci analytics,
kazda s vlastnimi udaji. Nove sluzby SAP Business One, Google Workspace
a Meta Ads. K tomu 23 skriptu, ktere s nimi opravdu neco delaji.
OpenAI jako prvni sluzba, ktera nebezi u nas: Service.baseUrl s absolutni
adresou, prepis pres <SLUZBA>_BASE_URL nebo adresu u konektoru, predpona
hlavicky u pole udaju (uzivatel vlepi holy klic, Bearer dopise runtime).
Dotaz na model, nahrani souboru, otazka nad souborem, prepis zvuku.
Skript umi odeslat soubor pres ctx.http.postForm (multipart, obsah Base64).
Sluzba E-mail pres SMTP. Neni to skript, ale vnitrni krok - SMTP neni HTTP.
Konektor nese schranku firmy, krok ma HTML telo, ve kterem se dosazene
hodnoty escapuji (znacky autora sablony jsou zamer, ostre zavorky od
zakaznika ne). Overeni konektoru se prihlasi na server a nic neodesle.
Helpdesk: Ticket.helpdeskSourceId drzi firmu, ktera pozadavek poslala,
vlastnikem zustava ta, ktera ho resi - jinak by ho resitel nemel ve sve
fronte. Komu pozadavek pripadne, urcuje Tenant.helpdeskProviderId.
Zadavatel vidi jen svoje pozadavky a smi k nim pripsat komentar.
Opravy v portalu:
- hlasky o ulozisti a odchozi IP vidi jen spravce platformy
- typ ticketu se v automatizaci vybira ze seznamu firmy, nebo dosadi z dat
- stav ticketu je otevreny naseptavac, ne ciselnik
- ticket jde zalozit rucne, zakaznik u nej neni povinny
- kanal se prejmenoval a parametry u webhooku jsou oznacene jako nepovinne
- srovnane markdown tabulky v cele dokumentaci
Co z teto davky jeste neni: prepinac firmy je porad jen stav uvnitr stranky
Prehled, takze se prepnuti neprojevi v Lidech ani jinde.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bezny postup nesel poskladat z kroku: najdi podle ICO, kdyz neni zkus mail,
kdyz porad neni zaloz - ID dava jednou jedna vetev a jednou druha, ale
rozsah vystupy z vetvi za podminku nepoustel. Slucovat kvuli tomu hledani
a zakladani do jednoho kroku bylo obejiti nasi chyby, ne reseni.
- Vystup z vetve je za podminkou k dispozici, jen jako nepovinny. Ze muze
chybet, se neztratilo: builder to u pole ukaze a pri behu se dosadi
prazdno.
- Vystup se stejnym jmenem uz z nabidky nemaze ten starsi. Po druhem
hledani kontaktu zmizelo ID z prvniho, tedy to, co je v tu chvili
potreba. Odkaz se jmenem kroku je jednoznacny.
- Duplicitni jmena u vystupu kroku uz nejsou nedodelek. Konflikt zustava
mezi parametry spoustece, kde zadny prefix neni.
- Novy krok Zalozit kontakt. Nic nedohledava, hledani je vlastni krok.
Najit nebo zalozit zustava pro toho, komu staci jistota jednim krokem.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
iDoklad ceka v tele `IssuedInvoiceItems`, ale prevod je casto pojmenuje
`items` podle toho, jak se jmenuji ve zdrojove objednavce. Kontrola trvala
na `items`, telo se ale posilalo tak, jak prislo - prevod s PascalCase
jmeny by tedy neprosel kontrolou, a prevod s malymi pismeny by prosel
a doklad by v iDokladu vznikl bez radku.
Ted se polozky hledaji pod obema jmeny a do tela se doplni pod tim, ktere
ceka iDoklad.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Chybelo k tomu, aby sla postavit cela cesta objednavka -> faktura.
- Novy skript idoklad.upsert-contact: dohleda odberatele podle ICO nebo
e-mailu, a kdyz neni, zalozi ho a vrati nove ID. Vystup `created` rekne,
co se stalo, takze na to jde navazat podminkou.
Proc jeden krok a ne dvojice ve vetvich podminky: vetev nepridava nic do
sekvence za podminkou, protoze nemusela probehnout. ID odberatele by za
podminkou nebylo k dispozici, i kdyz ho ve skutecnosti daji obe vetve.
Zaklada se az kdyz hledani nic nenaslo - opacne poradi by pri kazde
objednavce vyrobilo dalsiho odberatele se stejnym ICO.
- Krok Vlastni skript ma druhy vstup "Co pridat ke vstupu". Skript casto
potrebuje krome hlavniho objektu jednu hodnotu z predchoziho kroku,
typicky ID odberatele. Prijde v input.extra, aby se hlavni data nemichala
s tim, co jsme doplnili my.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Transformace dat ve dvou rezimech plus oprava chyby, kvuli ktere se neukladaly
pristupove udaje konektoru. Popis v documentation/13-transformace-dat.md.
Kroky si predavaji i cele struktury:
- FieldType ma object a list. Do sablony se nedosazuji, predavaji se jako celek
dalsimu kroku - proto je u nich v builderu vyber a ne textove pole. Z podminek
nad nimi ma smysl jen "prisla / neprisla"
- strop na velikost struktury (SCRIPT_MAX_VALUE_BYTES, vychozi 256 kB). Radek
s vystupem kroku je nejrychleji rostouci tabulka v systemu
Dva rezimy transformace, oba nad enginem v src/scripts/mapping.ts:
- transform.map-fields: pole na pole s prevody, klikatelne
- transform.to-json: sablona cileveho objektu s ${cesta}
Marker ${...} je zamerne jiny nez {{...}}. Sablony kroku se dosazuji driv, nez
krok bezi, takze {{total}} by strom stihl vyhodnotit, nenasel by parametr toho
jmena a dosadil by prazdno. Cely retezec navic zachova typ, takze
"unitPrice": "${total}" vyrobi cislo - jinak by cizi sluzba dostala castku jako
text a odmitla ji.
Prevod map pro seznamy je to, bez ceho by priklad nesel dokoncit. Bez nej jde
prevest hlavicku dokladu, ale ne polozky objednavky, a doklad by byl na nulu.
Dal pridano:
- idoklad.create-invoice-from-object: druha polovina prikladu, bere hotove telo
dokladu z transformace a doplni povinna pole ze vzoru iDokladu
- spoustec e-shopu predava celou objednavku jako objekt a polozky jako seznam
- klikaci editor pravidel vcetne rezimu JSON pro vnorena pravidla u map
- kontrola JSONu a tvaru pravidel uz pri ulozeni stromu. Preklep je nedodelek,
ne chyba ukladani - rozdelana prace se nezahazuje
Opraveno: konektor neukladal pristupove udaje. Server byl v poradku, overeno
volanim POST i PATCH. Chyba byla v prohlizeci: u pole type="password" prohlizec
ignoruje autocomplete="off" a dosazuje ulozene prihlaseni. Uzivatel pak videl
jednu hodnotu, React drzel jinou, a ulozilo se to, co drzel React, tedy nic.
Resi to autocomplete="new-password", jmena poli, ktera nepripominaji heslo,
a prepinac zobrazeni, aby slo overit, co je opravdu zapsane.
Zakladani a uprava konektoru se presunuly do dialogu, na strance jsou jen male
karty. Formulare rozlozene po strance byly u vic konektoru neprehledne.
Overeno: npm run typecheck prochazi na serveru i webu, node --check na skriptech.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Slovo "konektor" v kodu znamenalo katalog toho, co umime. Ted znamena napojeni
jedne firmy, tedy to, co tim mysli i uzivatel. Popis modelu je
v documentation/12-sluzby-a-konektory.md.
Tri vrstvy:
- Sluzba: ze iDoklad existuje, co umi a co potrebuje k napojeni. Nase.
- Skript: kod, ktery jednu operaci sluzby opravdu vykona. Nas.
- Konektor: ucet firmy vcetne jejich pristupovych udaju. Firemni.
Pristupove udaje se prestaly cist z environment variables. Cela instance by
mela jedny udaje spolecne a dve firmy by fakturovaly z jednoho uctu. Napojeni
je vlastnost firmy, ne prostredi. Z prostredi zustava jen SERVICES_BASE_URL.
Pridano:
- src/data/services.ts: sluzba nese general, appId, visibility, credentials
a verifyPath. Kategorie "obecne" sdruzuje veci, ktere ma kazdy a nepotrebuji
konektor: webhook, planovac, tickety, transformace dat, HTTP pozadavek,
pauza, zapis do logu
- viditelnost sluzby: vsichni, jen uvedene firmy a lide, nebo jen spravce
platformy. Neviditelna sluzba se z API nevraci vubec, ne se stavem 403 -
firma nema poznat, ze takova sluzba existuje
- src/data/connectorStore.ts: konektory za firmu vcetne hodnot udaju. Hodnoty
se z API nikdy nevraci, jen filled a missing. Prazdne pole hodnotu nemeni,
takze ulozeni formularu bez tajnych hodnot nic nepresepe
- FlowStep.connectorId: krok rika, pod kterym napojenim volat. null = vychozi
konektor firmy, diky tomu je vzorovy strom prenositelny mezi firmami
- overeni konektoru pres verifyPath, tedy cteci volani vyzadujici autorizaci.
U sluzby bez nej se overi jen dostupnost a odpoved to rekne nahlas, jinak by
zeleny vysledek uzivateli lhal
- stranky /dashboard/sluzby a /dashboard/konektory vcetne formularu udaju
- endpointy /api/dashboard/services a CRUD /api/dashboard/connectors ve Swaggeru
- predvyplnene prihlaseni spravcem platformy a prepinac demo uctu na login
strance, kvuli testovani prototypu
Zmeneno:
- stav "napojeno" se prestal cist z katalogu a zacal pocitat z konektoru firmy.
Sluzba ma jen available nebo planned
- validace stromu overuje i konektor. Cizi konektor je chyba, chybejici
napojeni nedodelek - rozdelana prace se nezahazuje
- prejmenovani napric kodem: Connector na Service, FlowStep.connectorId na
serviceId, GET /connectors na GET /services, connectorIcons na serviceIcons,
stranka Konektory (katalog) na Sluzby. Prevodni tabulka je v dokumentu 12
Overeno: npm run typecheck prochazi na serveru i webu.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Konektory dostaly vykonnou cast. Jeden skript je jeden soubor, ktery nese
manifest (vstupni a vystupni parametry) i kod. Diky manifestu s nim umi
pracovat strom automatizace, aniz by o kodu cokoliv vedel.
Soubory jsou zamerne obycejny JavaScript, ne TypeScript. TypeScript by se
musel prelozit a to je presne to otaceni, ktere tady nema byt. Registr
sleduje cas zmeny souboru, takze uprava v portalu, rucni uprava souboru
i novy soubor ve slozce funguji stejne a bez restartu.
Pridano:
- scripts/ se skripty konektoru, nazev souboru je zaroven ID operace
- kontrola vstupu i vystupu proti manifestu, jedna funkce pro obe strany.
Chybejici povinny vystup je chyba skriptu, ne uzivatele - jinak by strom
veril parametru, ktery nikdy nedosel
- ctx predavany skriptu: http nad adresou napojeni, util, log, config,
idempotencyKey, fail a retry. Skript nedostane pristupove udaje
- rozliseni opakovatelne a koncove chyby. Runner nikdy nevyhodi vyjimku,
vzdy vraci vysledek vcetne retryable
- redakce tajnych hodnot pred zapisem do logu. Cizi API rado vraci prijaty
token v chybove zprave a log ticketu vidi klient
- napojeni z environment variables vcetne iDokladu
- sest ukazkovych skriptu pro iDoklad proti skutecnemu API sluzby
services.csbot.cz/apps/idoklad, kazdy na jiny vzor
- stranka /dashboard/skripty: seznam, manifest, editor, zkusebni spusteni.
Formular testu se sklada z manifestu, nepise se pro kazdy skript
- endpointy /api/dashboard/scripts vcetne Swaggeru
Zmeneno:
- katalog konektoru uz neni jen staticky seznam. Akce ze skriptu se domeruji
prekryvem v src/data/connectors.ts, takze se naraz objevi ve validaci
stromu, ve vypoctu scope i v sablonach. Pri stejnem ID vyhrava skript
- ConnectorOperation ma implementation a scriptId
- ApiError na klientovi nese cele telo odpovedi a umi z nej vytahnout issues
- Dockerfile kopiruje scripts/ do vysledneho image
Ukladani nemuze rozbit fungujici skript: kod se nejdriv zapise do docasneho
souboru, ten se nacte a overi, a az pak prepise puvodni.
K tomu tri dokumenty navrhu dalsich kroku: 09 datove modely a prava,
10 runtime a rozpocet na 150 klientu, 11 popis skriptu konektoru.
Overeno: npm run typecheck prochazi na serveru i webu.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>