Authenticated command gateway + CLI + MCP wrapper (#57) - #40
Open
pplupo wants to merge 55 commits into
Open
Conversation
Implements the allowlist table, GatewayCommandRunner (CDP Runtime.evaluate over QtWebSockets), GatewayServer (authenticated unix-socket transport), and the eo-ctl CLI per cdp-gateway-cli-plan.md. Word command family: word.getTitle/setTitle, word.getCustomProperty/ setCustomProperty (ApiCore/ApiCustomProperties), matching gateway-test-case-designs.md section B1. Known gaps, not silently papered over: - GatewayCommandRunner::ResolveTargetWebSocketUrl is a stub that always returns TARGET_NOT_FOUND; correlating a CDP target to an app view id via CAscApplicationManager's m_mapViews needs a follow-up look at the vendored CEF sources (see the KNOWN GAP comment in gatewaycommandrunner.h). - GatewayServer is not yet instantiated from the app's startup path. - word_document_properties_test.cpp currently only exercises schema validation (blocked on the above two gaps for full round-trip cases). Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
…rtup Drops QtWebSockets and the external-CDP-port approach in favor of CCefView::SendGatewayDevToolsMessage (desktop-sdk). GatewayCommandRunner now resolves targets via CAscApplicationManager::GetViewById directly -- no CDP target-correlation step needed, since we already hold the CefBrowser through the resolved view. Registers the Word command family and starts GatewayServer once from main.cpp, right after AscAppManager::startApp(), per investigation of the app's startup sequence. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
word.getAllParagraphs/getAllTables/getAllDrawingObjects/getAllCharts, backed by ApiDocumentContent's GetAll* methods (ApiDocument inherits ApiDocumentContent, apiBuilder.js:3112). Each returns an array of 0-based indices rather than the Api* object instances the underlying method returns, since those aren't JSON-serializable over CDP's returnByValue -- corrected gateway-test-case-designs.md §B2 to match. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
word.addText/getText, backed by ApiDocumentContent.GetElement + ApiParagraph.AddText/GetElement + ApiRun.GetText (apiBuilder.js:6028, 10146, 10315, 12883). AddText always appends a new run rather than editing one in place, per the underlying API's own documented behavior -- not assumed, read directly. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
word.setBold/setItalic/setFontFamily/setColor, backed by ApiRun methods (apiBuilder.js:12457,12550,12614; SetColor at 12507 forwards to ApiTextPr.SetColor at 15918, whose non-ApiColor branch still accepts plain r,g,b ints -- used directly rather than constructing an ApiColor, since the scope's #RRGGBB hex already has to be decomposed into r/g/b regardless). setColor's schema constrains scope.color to a Signed-off-by: Peter P. Lupo <pplupo@gmail.com> #RRGGBB pattern, matching gateway-test-case-designs.md §B4.6.
word.setJc/setSpacingBefore/setIndLeft, backed by ApiParagraph.GetParaPr + ApiParaPr methods (apiBuilder.js:10253,16580,16677,16863). All three take twips (1/1440 inch), not points -- scope field renamed from the plan's original 'points' to 'twips' to match the real unit, and gateway-test-case-designs.md §B5 corrected accordingly. setIndLeft allows negative twips (hanging indent), confirmed against SetIndLeft's own lack of a sign check. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
word.search/searchAndReplace, backed by ApiDocument.Search/ SearchAndReplace (apiBuilder.js:7598,8253). Search returns the match count rather than the underlying ApiRange[] (not JSON-serializable over returnByValue, same issue as §B2) -- corrected gateway-test-case-designs.md §B6 to match. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
word.addRow/addColumn/mergeCells/setStyle, backed by ApiTable methods (apiBuilder.js:13589,13609,13666,13755,13811) and ApiDocument.GetStyle (7091). tableIndex indexes GetAllTables() (same space as word.getAllTables, §B2). AddRow/AddColumn take a cell to insert relative to, not a row/column number -- resolved via GetCell(rowIndex/colIndex, 0) then inserted after it. MergeCells takes an ApiTableCell[] built from the fromRow/fromCol..toRow/toCol rectangle. SetStyle takes an ApiStyle object, resolved via ApiDocument.GetStyle(styleId). Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
word.createStyle/getStyle/setStyleTextPr, backed by ApiDocument.CreateStyle/GetStyle (apiBuilder.js:7091,7106), ApiStyle.SetTextPr (15424), Api.CreateTextPr (27443). getStyle returns a boolean (ApiStyle.Style is undefined for a nonexistent style) rather than the unserializable ApiStyle handle. Resolved gateway-test-case-designs.md §B8's two open decisions: duplicate createStyle is idempotent per CreateStyle's own doc comment (confirmed in source), and getStyle's wire shape is boolean not a handle. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
word.createImage/setWrappingStyle/setHorPosition, backed by Api.CreateImage (apiBuilder.js:4674), ApiParagraph.AddDrawing (10486), ApiDrawing.SetWrappingStyle/SetHorPosition (18651, 18771). Corrected gateway-test-case-designs.md §B9: createImage's imageSrc is a URL or base64 data URI per the method's own doc comment, not a local file path as originally planned; added the missing paraIndex/target paragraph the original design omitted. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
word.setHeaderText/setPageMargins/setPageSize, backed by ApiDocument.GetFinalSection (apiBuilder.js:7191), ApiSection.GetHeader/SetPageMargins/SetPageSize (13141,13181,13289), Api.CreateParagraph (4575), ApiDocumentContent.AddElement (6052). Dropped the originally-planned sectionIndex scope field -- ApiDocument has no GetSection(index), only GetFinalSection() -- correct for every single-section fixture in this document; corrected gateway-test-case-designs.md §B10 accordingly. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
word.addBookmark/getBookmark/addHyperlink, backed by ApiParagraph.GetRange (apiBuilder.js:10604) + ApiRange.AddBookmark (1581), ApiDocument.GetBookmark (8907), ApiParagraph.AddText+ AddHyperlink (10578). Corrected gateway-test-case-designs.md §B11: AddHyperlink takes no display-text parameter -- it wraps whatever text is already in the paragraph -- so text is added first, then wrapped. URL scheme allowlist (http/https/mailto) enforced at the gateway's own schema layer as defense-in-depth. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
word.addTextForm/getAllForms/setFormsData, backed by ApiDocument.InsertTextForm (sdkjs-forms/apiBuilder.js:452), GetAllForms/SetFormsData (sdkjs/word/apiBuilder.js:8613,7963), ApiFormBase.GetFormKey (25123). word.addCheckBoxForm intentionally NOT implemented -- no confirmed insertion method exists in the vendored source for a created ApiCheckBoxForm (AddInlineLvlSdt's own type guard would reject it); flagged as an open item in gateway-test-case-designs.md §B12 rather than guessed. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
word.addComment/getAllComments/setTrackRevisions/
acceptAllRevisionChanges, backed by ApiDocument.AddComment/
GetAllComments/AcceptAllRevisionChanges (apiBuilder.js:9583,8702,8856)
and ApiComment.GetText/GetAuthorName (27848,27877). AddComment adds
to the current selection, positioned via the same GetRange(0,0).Select()
pattern as §B12; getAllComments returns plain {text,author} objects
rather than the unserializable ApiComment handles.
This completes all 13 Word command families (§B1-§B13) per
cdp-gateway-cli-plan.md's build order, aside from the one deferred
word.addCheckBoxForm (§B12, flagged, not guessed). Next: the plan's
§6 per-editor build/deploy/regression gate before starting Cell.
Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
Discovered as a real gap by the plan's §6 build/deploy gate: eo-ctl built fine as a CMake target but had no install() rule, so it never made it into the app's install/deploy bundle (cmake --install .), unlike DesktopEditors/editors_helper. Added to the same install(TARGETS ...) list at the app root, matching how those targets are installed. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
Gateway tests (gateway_word_*_test) are headless -- Qt Core only, no CEF/display needed -- so they can run right after cmake --build in the same containerized build step, gating cmake --install (and thus packaging) on them passing, per cdp-gateway-cli-plan.md §6. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
cell.addSheet/getSheets/setActiveSheet/getActiveSheet/setVisible/ setName, backed by Api.AddSheet/GetSheets/GetSheet/GetActiveSheet (sdkjs/cell/apiBuilder.js:777,799,867) and ApiWorksheet.SetActive/ SetVisible/SetName/GetName (8332,8314,8557,8546). getSheets/ getActiveSheet return sheet names rather than unserializable ApiWorksheet handles, same pattern as the Word families. First Cell family per cdp-gateway-cli-plan.md §4 build order -- Word is fully implemented and passed its §6 build/deploy/test gate. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
cell.setValue/getValue/getFormula, backed by ApiWorksheet.GetRange (apiBuilder.js:8602) and ApiRange.SetValue/GetValue/GetFormula (10161,10132,10241). Corrected gateway-test-case-designs.md §C2: there is no separate SetFormula -- a string value starting with '=' becomes a formula through the same SetValue call, so setValue's scope merges down to one 'value' field. GetFormula returns '= ' + text with a literal space, preserved rather than assumed away. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
cell.setNumberFormat/merge/clearContents, backed by ApiRange methods (apiBuilder.js:10828,10897,9759), all of which return null/undefined on success. cell.merge's across field maps to Merge's isAcross param. No read-back command exists yet for formatted display text (only cell.getValue, which returns the raw value) -- C3.1's read-back deferred to the §6 build/deploy gate rather than adding a speculative command. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
cell.copy/find/replace, backed by ApiRange.Copy/Find/Replace (apiBuilder.js:11338,11616,11764) over ApiWorksheet.GetUsedRange() (8524). Corrected gateway-test-case-designs.md §C4: Find/Replace are per-range methods returning a single ApiRange|null (first match), not a document-wide multi-match search -- the original 'returns 2 matches' expectation didn't correspond to any real capability of these methods. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
cell.setFontName/setFillColor/setBorders/setAlignHorizontal, backed by ApiRange methods (apiBuilder.js:10513,10772,10575, SetBorders per §C3 investigation). SetFillColor/SetBorders need a real ApiColor built via Api.CreateColorFromRGB (925), same hex decomposition as word.setColor. setBorders' edge:"all" is handled by looping over the four outer edges in script -- SetBorders itself has no such mode. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
cell.addColorScale/addDatabar/addIconSetCondition, backed by ApiRange.GetFormatConditions (apiBuilder.js:12827) and ApiFormatConditions.AddColorScale/AddDatabar/AddIconSetCondition (21119,21229,21299), all returning a boolean rather than the unserializable created-rule objects. AddIconSetCondition takes no parameters -- dropped the originally-planned iconSet scope field rather than guess at how icon-set type is actually configured. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
cell.addValidation/addDefName, backed by ApiRange.GetValidation().Add (apiBuilder.js:12793,19898) and ApiWorksheet.AddDefName (8974). Corrected gateway-test-case-designs.md §C7: validation type/operator use the real internal enum strings (xlValidateWholeNumber, xlBetween), not the originally-planned whole/between. AddDefName returns false rather than throwing for an invalid name -- converted to a thrown error in the command's script to keep this gateway's own error contract consistent. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
cell.applyFilter/getFilters. Corrected gateway-test-case-designs.md §C8: ApiAutoFilter.ApplyFilter (apiBuilder.js:27379) only re-evaluates an existing AutoFilter's criteria, it does not create one -- creating one is ApiRange.SetAutoFilter() called with no args (12216), a toggle (deletes if one already exists). getFilters returns GetFilterMode()'s boolean rather than the unserializable ApiFilter[]. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
cell.addPivotTable/addPivotDataField/setPivotFieldFunction. Real workflow is three distinct steps, not the single-call shape originally planned: Api.InsertPivotExistingWorksheet (apiBuilder.js: 7676) creates + names the table (ApiPivotTable.SetName, 16782) so ApiWorksheet.GetPivotByName (9412) can re-resolve it in later, separate gateway calls -- there's no addressing by source range. ApiPivotField.SetFunction (the field type AddFields works with) is a hardcoded-error stub; the real setter is ApiPivotDataField.SetFunction (17582), via AddDataField (16192) or GetDataFields (16633) re-resolve. func takes capitalized enum strings (Sum, Average, ...), not lowercase. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
cell.freezeAt, backed by ApiWorksheet.GetFreezePanes().FreezeAt (apiBuilder.js:9474,15780). Range resolved explicitly via ws.GetRange() rather than FreezeAt's own string-overload, which resolves against the active sheet, not necessarily this command's target sheet. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
cell.addImage/addOleObject, backed by ApiWorksheet.AddImage/ AddOleObject (apiBuilder.js:9167,9228) -- placed by column/row + EMU offset, not a range; imageSrc is URL/base64, matching word.createImage (§B9). cell.addShape intentionally NOT implemented -- AddShape (9146) needs real ApiFill/ApiStroke objects whose constructors weren't confirmed in this file this pass; flagged in gateway-test-case-designs.md §C11 rather than guessed. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
cell.addComment/addReply/setSolved, backed by ApiRange.AddComment (apiBuilder.js:10969), ApiComment.AddReply/SetSolved/GetId (14189,14019,13921). ApiComment has no public row/col accessor -- addReply/setSolved address a comment by the id addComment/GetId returns, resolved via ws.GetComments().find(...), not by range as originally planned (no real lookup for that exists). Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
cell.insertEntireRow/deleteEntireColumn, backed by ApiWorksheet.GetRangeByNumber (apiBuilder.js:8642, 0-based, matching this document's index convention) + ApiRange.GetEntireRow/ GetEntireColumn/Insert/Delete (12753,12775,11280,11241). Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
cell.recalculateAllFormulas, backed by Api.RecalculateAllFormulas (apiBuilder.js:7586) -- matches the plan's original design exactly, no correction needed. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
cell.addSeria/setSeriaName, backed by ApiChart.AddSeria/SetSeriaName (apiBuilder.js:13641,13608), addressed via ApiWorksheet.GetAllCharts()[chartIndex] (9359) -- the only real addressing option, since ApiChart has no GetName/SetName at all. AddSeria/SetSeriaName take range strings, not the single range/name scalars originally planned for a couple of params. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
cell.getSmartArtClassType, backed by ApiSmartArt.GetClassType (apiBuilder.js:13307/13294), addressed via ApiWorksheet.GetAllDrawings()[index] (9271) -- SmartArt is one variant of the generic Drawing typedef, there is no SmartArt-only collection. This completes all 16 Cell command families (§C1-§C16) per cdp-gateway-cli-plan.md §4's build order, aside from two deliberately deferred commands (cell.addShape §C11, word.addCheckBoxForm §B12 from the Word family) flagged rather than guessed. Next: the plan's §6 per-editor build/deploy/regression gate before starting Slide. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
slide.addSlide/removeSlides/duplicate/moveTo, backed by ApiPresentation.AddSlide/RemoveSlides/GetSlideByIndex (sdkjs/slide/apiBuilder.js:1365,1564,1324), Api.CreateSlide (805), ApiSlide.Duplicate/MoveTo (3853,3872). Corrected gateway-test-case-designs.md §D1: RemoveSlides takes a contiguous start+count range, not an arbitrary indices array, and returns false rather than throwing for an out-of-range start -- converted to a thrown error in the command's script. First Slide command family per cdp-gateway-cli-plan.md §4 build order -- Word and Cell are both fully implemented and passed their §6 gates. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
slide.getAllShapes/getAllImages/getAllTables/getAllCharts, backed by ApiSlide's matching GetAll* methods (apiBuilder.js:4140,4155,4197, 4169) -- matches the plan exactly. Returns index arrays rather than the unserializable Api* object arrays, same pattern as word.getAllTables (§B2). Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
slide.getLayout/applyLayout/addMaster, backed by ApiSlide.GetLayout/ApplyLayout (apiBuilder.js:4092,3800), ApiPresentation.AddMaster (1522), Api.CreateMaster (553). applyLayout borrows an already-resolved layout from another slide rather than a fabricated layoutId lookup that doesn't exist. slide.applyTheme intentionally NOT implemented -- Api.CreateTheme (640) needs three further factory-built scheme objects not confirmed in this pass; flagged in gateway-test-case-designs.md §D3. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
slide.setTransition, backed by Api.CreateSlideShowTransition (apiBuilder.js:1075) + ApiSlideShowTransition.SetEntryEffect/ SetDuration (4848,4902) + ApiSlide.SetSlideShowTransition (4389). Field renamed from type to entryEffect to match SetEntryEffect's real param name; false return converted to a thrown error, same pattern as elsewhere. slide.setBackground intentionally NOT implemented -- no solid-fill factory (Api.CreateSolidFill or equivalent) was confirmed in sdkjs/slide/apiBuilder.js; flagged in gateway-test-case-designs.md §D4. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
slide.createShape/setPosition/setRotation/setSize, backed by Api.CreateShape (apiBuilder.js:870, has real fill/stroke defaults, unlike Cell's AddShape), ApiSlide.AddObject (3621), ApiDrawing.SetPosition/SetRotation/SetSize (6100,6511,6079). Corrected gateway-test-case-designs.md §D5: CreateShape takes no position -- createShape's x/y map to a follow-up SetPosition call in the same command script. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
slide.setBold/setFontFamily, reusing ApiRun.SetBold/SetFontFamily (shared classes with Word, confirmed absent from sdkjs/slide/apiBuilder.js itself) with slide-side target resolution via ApiShape.GetContent() (apiBuilder.js:6975) + GetElement(paraIndex).GetElement(runIndex), same chain as word.setBold (§B4). Added the missing paraIndex the original scope lacked. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
slide.createImage, backed by Api.CreateImage (apiBuilder.js:825), matching the Word/Cell precedent exactly (URL/base64, not a file path). Same AddObject+SetPosition two-step as slide.createShape (§D5). Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
slide.addRow/mergeCells, backed by ApiTable.AddRow/MergeCells (apiBuilder.js:7412,7314) on an existing table addressed via slide.GetAllTables()[tableIndex] (§D2). Cell resolution via GetRow(r).GetCell(c) (7295,7614) -- slide's ApiTable has no GetCell(row,col) shortcut. slide.createTable intentionally NOT implemented -- Api.CreateTable (947) places on whatever slide is 'current', with no public setter to target one by index; flagged in gateway-test-case-designs.md §D8. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
slide.addNotesText/getNotesText, backed by ApiSlide.AddNotesText (apiBuilder.js:4331, calls ApiParagraph.AddText internally, same append-only semantics as word.addText §B3) and GetNotesPage().GetBodyShape().GetDocContent().GetElement(0).GetText() for read-back. Added slide.getNotesText, not in the original design at all -- without it D9.1's expectation had no allowlisted command to verify with. Resolved D9.2's append-vs-replace question definitively: appends, confirmed in source. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
slide.addComment/presentation.getAllComments, backed by ApiSlide.AddComment (apiBuilder.js:3649) and ApiPresentation.GetAllComments (1697, shared ApiComment class with Word, §B13). Added missing x/y EMU position fields the original scope lacked. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
presentation.getDocumentInfo/getCustomProperty, backed by
ApiPresentation.GetDocumentInfo (apiBuilder.js:1849, returns a plain
JS object -- already JSON-safe) and GetCustomProperties (1936, same
shared ApiCustomProperties class as Word §B1, no GetAll -- corrected
to singular getCustomProperty{name}).
This completes all 11 Slide command families (§D1-§D11) per
cdp-gateway-cli-plan.md §4's build order, aside from four deliberately
deferred commands (slide.applyTheme §D3, slide.setBackground §D4,
slide.createTable §D8) flagged rather than guessed, matching the same
discipline applied to word.addCheckBoxForm (§B12) and cell.addShape
(§C11). Next: the plan's §6 per-editor build/deploy/regression gate
before starting PDF.
Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
pdf.getAllFields/getFieldValue/setFieldValue, backed by ApiDocument.GetAllFields/GetFieldByName (sdkjs/pdf/apiBuilder.js:1426, 1452) and the shared ApiBaseField.GetValue/SetValue (1921,1902) -- uniform across text/checkbox/combobox field types. getAllFields returns field names, not the unserializable ApiField objects. Corrected gateway-test-case-designs.md §E1: SetValue stringifies its argument, so a checkbox's checked state is a string export value (e.g. "Yes"/"Off"), not a JSON boolean; an unknown key surfaces as SCRIPT_EXCEPTION from GetFieldByName's own unchecked .IsWidget() access, no extra logic needed. First PDF command family per cdp-gateway-cli-plan.md §4 build order -- Word, Cell, and Slide are all fully implemented. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
Adds pdf.addHighlight, pdf.addUnderline, pdf.addStrikeout, pdf.addFreeText,
pdf.addInk, and the read-back command pdf.getAllAnnots. Each creation command
maps to the matching Api.Create*Annot() factory plus ApiPage.AddObject();
ink paths are accepted as [x,y] pairs and converted to the {x,y} object shape
CreateInkAnnot requires. pdf.addStamp is deliberately not implemented --
AscPDF.STAMP_TYPES' real values were not located in the vendored source.
Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
Adds pdf.searchText, pdf.setSelection, pdf.getSelectedText, and pdf.recognizeContent. searchText's Quad results are already flat JSON-safe arrays, unlike every other Api* read command in this file. recognizeContent's ApiDrawing[] result is converted to class-type strings, same read-back pattern used for annotations in §E2. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
Adds pdf.addRedact (creates a pending redact annotation), pdf.searchAndRedact (marks every doc-wide text match as pending), and pdf.applyRedact (strips all pending redacts, document-wide -- there is no per-page apply). Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
Adds pdf.addPage, pdf.removePage, and pdf.getPageCount. addPage's ApiPage result is converted to its index; removePage's false-on-out-of-range return is converted to a thrown error for the consistent SCRIPT_EXCEPTION contract. This completes the PDF command family (Word -> Cell -> Slide -> PDF). Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
Every gateway_*_test executable failed at runtime with 'libQt6Core.so.6: cannot open shared object file' -- this is the first build that ever actually exercised the ctest step (the Dockerfile's ctest line hadn't reached the server checkout for any earlier gateway build), so this bug was never caught before now. Unlike the app's real targets, these test executables never go through set_default_options() (core/common.cmake) or the app's install step, so they got no RPATH and no copy of Qt's vcpkg-built .so files. Setting a directory-scoped CMAKE_BUILD_RPATH, derived from Qt6::Core's actual imported location, fixes every target in this file with one change. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
The compile step's verbose warning output was filling BuildKit's 2MiB per-step log buffer before ctest's own results streamed, repeatedly hiding per-test detail behind '[output clipped, log limit 2MiB reached]'. Isolating ctest+install+package into a separate layer, reusing the same build-cache-desktop and ccache mounts, keeps its log independent of the compile step's noise. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
…'s LOCATION property get_target_property(Qt::Core LOCATION) silently resolved to empty -- Qt's vcpkg-generated imported target only populates IMPORTED_LOCATION_<CONFIG>, not the generic LOCATION, with no CMAKE_BUILD_TYPE set. Deriving the lib dir directly from vcpkg's own install layout instead (confirmed against the build log: every vcpkg-built .so, Qt included, lands under CMAKE_BINARY_DIR/vcpkg_installed/<triplet>/lib), plus a status message so this is visible at configure time instead of failing silently again. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
CMAKE_BUILD_RPATH had no effect: core/common.cmake sets CMAKE_BUILD_WITH_INSTALL_RPATH TRUE globally, which makes build-tree binaries use each target's INSTALL_RPATH property instead of the auto-computed build RPATH -- CMAKE_BUILD_RPATH is ignored entirely under that setting. CMAKE_INSTALL_RPATH (seeding INSTALL_RPATH for targets created afterward) is what actually takes effect without an install step. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
…r RPATH Ground truth from debugging: Qt6 here comes from aqtinstall (third_party/install/qt/<version>/<arch>), not vcpkg -- vcpkg's install tree has no Qt in it at all. QT_ROOT is already computed globally by core/common.cmake for exactly this purpose; using it directly instead of guessing vcpkg's layout. Also reverts the temporary debug find/readelf commands added to the Dockerfile while diagnosing this. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
…lve a real id
Closes the gap found while designing the MCP wrapper (~/repos/eo-mcp-service-plan.md):
'eo-ctl connect <file>' previously only checked whether *a* gateway socket existed at
all, never whether the requested file was actually open, and returned an exit code
with no way for a caller to learn which view id corresponds to the file it just asked
to open.
- GatewayCommandRunner::ResolveViewIdByPath: pure resolver via
CAscApplicationManager::GetViewByUrl, normalizing the path the same way
CAscApplicationManagerWrapper::handleInputCmd does before a view's local-file URL is
set.
- gateway.connect: new meta-command (same pattern as gateway.listCommands) exposing
that resolver over the wire. Deliberately never opens anything itself.
- eo-ctl connect: now actually opens the file via SingleApplication (confirmed against
main.cpp/cascapplicationmanagerwrapper.cpp: a second launch already forwards its file
arg to a running instance, which opens it as a new tab via handleInputCmd -- cold
start and already-running are handled by the same subprocess launch either way) and
polls gateway.connect until it resolves, printing {"targetViewId": N} instead of
just an exit code.
- The poll/launch-decision algorithm is factored into connectlogic.h/.cpp
(EoCtl::ConnectAndResolveViewId), dependency-injected for unit testing without a live
process -- see tools/eo-ctl/tests/connectlogic_test.cpp (6 cases).
Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
This was referenced Aug 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Authenticated command gateway + CLI + MCP wrapper
Implements #57 (Local API / Daemon Mode): an authenticated, allowlisted command
gateway for Word/Cell/Slide/PDF, a thin CLI (
eo-ctl) around it, and a thin MCPservice (
eo-mcp) around that. Depends on the companiondesktop-sdkPR (thein-process CDP bridge this gateway drives).
Architecture
AF_UNIX SOCK_STREAMsocket at$XDG_RUNTIME_DIR/eo-gateway-<uid>.sock(QLocalServer), mode0600. One JSONobject per connection, newline-terminated, one-shot request/response.
$XDG_RUNTIME_DIR/eo-gateway-<uid>.token(mode0600).template (
%%SCOPE%%as the sole substitution point, filled viaQJsonDocument::toJson, never raw string interpolation) — matches #57's explicit"parameters passed via JSON
scopeinjection, never raw string interpolation"requirement.
CCefView::SendGatewayDevToolsMessageagainst a view resolved through the app's own
CAscApplicationManagerview map — noexternal CDP target-matching by URL/title.
sdkjs/*/apiBuilder.jssource (notguessed) — every scope field, return-shape conversion (unserializable
Api*objectsconverted to indices/ids/class-type strings), and error-contract decision
(
SCRIPT_EXCEPTIONvsSCHEMA_INVALID) is backed by reading the actual vendoredAPI. A handful of commands (
word.addCheckBoxForm,cell.addShape,slide.applyTheme,slide.setBackground,slide.createTable,pdf.addStamp) aredeliberately left unimplemented where the required factory/enum couldn't be
confirmed in the vendored source, rather than guessed.
eo-ctl: thin CLI —connect <file>(resolves a file to a stabletargetViewId, opening it if needed),call <command> --scope '<json>',allowlist. No business logic beyond framing requests; the one non-trivial piece(the connect/poll algorithm) is factored into a dependency-injected, unit-tested
module (
tools/eo-ctl/tests/connectlogic_test.cpp).eo-mcp: a standalone Node MCP server (tools/eo-mcp/, not part of the CMakebuild) exposing three tools —
gateway_connect,gateway_call,gateway_list_commands— talking directly to the gateway socket.Command coverage
Word (13 families): document properties, content enumeration, insert/edit text,
character/paragraph formatting, search & replace, tables, styles, images/shapes,
page setup, bookmarks/hyperlinks, form fields, comments/track changes.
Cell (16 families): sheet management, range read/write, number formats/merge/clear,
copy/find/replace, formatting, conditional formatting, validation/named ranges,
AutoFilter, PivotTable, freeze panes, images/OLE, comments, insert/delete rows/cols,
recalculate, charts, SmartArt.
Slide (11 families): slide management, content enumeration, layouts/masters,
transitions, shapes, text formatting, images, tables, speaker notes, comments,
document properties.
PDF (5 families): form fields, annotations, text search/selection/extraction,
redaction, page operations.
Plus
gateway.connectandgateway.listCommandsmeta-commands for target resolutionand command discovery.
Testing
46 automated
ctesttargets (one per command family +eo_ctl_connectlogic),schema-validation-level coverage per the established pattern in this suite (round-trip
verification against a real running document is the CI/build gate's job). All 46 pass
in a full server build against this branch's own base
(
feature/wayland-migration-derived); this specific rebase ontomainwasconflict-resolved by tracing each commit's original diff rather than blindly accepting
either side, and re-verified with standalone syntax checks + the full 11-test
eo-mcpsuite, but has not yet been re-validated with a full CEF/Qt build againstmainspecifically — flagging that honestly for CI/review to catch anything therebase might have missed.
Docs
Full command reference (every
scopefield, return shape, error case, CLI/MCP usageexamples) is being maintained in the companion
DesktopEditorssuperproject PR'sgateway-api-reference.md.Companion PRs: desktop-sdk#12 · DesktopEditors#72