Commit Graph
27 Commits
Author SHA1 Message Date
Luis MartinezandClaude Opus 5.5 dddf60d79e Add audited, versioned MCP save workflow (#1452)
- `--export ... --target-version`, and explicit target_format /
  target_version on automation and protocol saves; a conversion that
  would drop unsupported records is refused unless allow_lossy is set.
- `audit` read op: dangling layers/blocks, duplicate handles, non-finite
  bounds, serialization, lossy records and the source DXF handle graph.
- `save_verified`: writes the requested version from the same snapshot a
  normal save writes, reopens it, checks the version and the entity
  manifest, audits the ASCII DXF handle graph and returns a SHA-256.
- ZOOM EXTENTS includes SDF text glyphs.
- Live MCP acceptance script and an optional reference-console audit
  runner under docs/automation.

Squashed from the PR with review fixes: merged with the DWT save path,
the save snapshot, the tool lists added since, and vendor-neutral
harness names; the dated verification record is left out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 16:49:11 +03:00
huanldandClaude Opus 5.5 279a87418e feat(automation): entity, block, publishing and REST operations from the fork
The operations docs/automation/API-SPEC.md (PR #1383) describes, brought
over from huaninstratech/OpenCADStudio so the specification no longer runs
ahead of the code:

- entity CRUD: entities_create, entities_delete, entities_transform,
  entities_copy_to, and `where` filters on query
- blocks and export: block_define, block_delete, a normalised wblock
- publishing: layout_create, page_setup_set, plot (per_page included)
- metadata: sysvar, xdata_set / xdata_get, file_identity
- collections: group_create, selection_set_save / load
- control plane: batch, events, view_focus, close
- the REST transport: `--http <port>`, the /api/v1 routes and their
  OpenAPI 3 description
- person-in-the-loop picks: user_select and getpoint, hosted by the GUI
  over the same REST routes, with on-screen pending signals

Taken from the fork's commits b7a7ed80, 69396b42, 64f1cf6a, c3aafc8e,
bb5a7bfa, 52317a5c, 50d293ba, b90e194f, 517c6397, 53a5c4c9, a49ac045,
37de5a46, a78b1822, 6d9aed2a, 33f522da, 5b5cafe2, 02a7e78d, 94df40dc,
fd5bc14d, 485fac61 and 2c5fc994.

Left out on purpose: 37de5a46 also removed the donation surface (the
Patreon fetch, the donation prompt, the Start-page supporters rail, the
DONATE command and its locale strings) and replaced the About dialog's call
to action; that is the fork's own product decision and stays out. The
fork's version bump and its unrelated IFC/BIM work are not part of this.

Adapted while porting: the REST server and the GUI bridge are desktop-only
and a template start is refused in the web build, so the wasm target still
compiles; the two plot tests read the PDF through the shared stream helper,
because exported streams are Flate-compressed; the benches set the new
crosshair field; comments and docs describe each operation in its own
terms rather than by another product's API names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 13:41:07 +03:00
Hakan SevenandClaude Fable 5.1 75e7f825cd docs(automation): make native.md match the code
The page merged from #1401 described --serve and --mcp as one surface.
Rewritten against src/app/automation.rs, src/app/control/mod.rs and
src/mcp.rs: the --serve readiness banner and --port mode, the two request
styles on that channel and how each treats a waiting command
(command_busy versus replacement), measure being available headlessly
while capture is not, --export writing DWG for every extension but .dxf,
startup dialogs belonging to the editor behind --mcp only, MCP sessions
being shared rather than one editor per connection, the MTEXT editor's
own actions, and the legacy run reply fields (blocked_by, unconsumed)
that only that path returns.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-20 20:30:46 +03:00
Hakan Seven dece694478 Merge pull request #1401 from buyun14/docs/automation-native-builds
docs(automation): document the native entry points and their limits
2026-09-20 20:28:14 +03:00
Hakan Seven 97b471949c Merge pull request #1394 from helenkwok/docs/automation-web-build
docs(automation): document driving the web build
2026-09-20 20:14:10 +03:00
buyun14 747245a75f docs(automation): document the native entry points and their limits
Refs #1349. The automation README describes `--mcp` and never mentions
`--serve`, even though every native build accepts it and it is the only channel
that works without a display. It also leaves out the two startup dialogs, the
`document_id` routing, and what `waiting_input` actually means.

`docs/automation/native.md` covers, all measured on a Linux headless machine
with `OpenCADStudio 2026.37+165`:

- choosing between `--mcp` and `--serve`, and what each needs (display,
  no-display, `--export` for one-shot conversion);
- the writable `HOME`/`XDG_CONFIG_HOME` requirement, without which
  `ocs_sessions` answers `No user configuration directory`;
- the startup dialogs: `AssocPrompt` then `DonationPrompt`, `new` answering
  `ok` with no document until `close_modal` clears them, and that the donation
  prompt appears once per application version per profile;
- targeting a document: reads and writes follow the active tab, so
  `document_id` has to be passed explicitly after `new`;
- feeding a whole line: `waiting_input` means more input is required (not that
  nothing happened), `cancel` closes a command, and the `unconsumed` /
  `blocked_by` fields added in #1392;
- the interactive `start`/`input` shapes, including that a typed value goes
  through `kind: "text"` while a keyword uses `kind: "token"` with its payload
  in a field named `text`, and that the `TEXT` content step belongs to the
  in-place editor (`text_input` / `text_commit`);
- what only native builds have: `measure`, `ocs_capture` (inline base64, or a
  PNG path), and `set_properties` with `collection` and colours as
  `{"Index": n}` / `"ByLayer"`;
- the remaining gap: `MTEXT`, `HATCH` and `POLYGON` report `completed` with
  `added: 0` on the `--serve` path (#1390).

The README gains one line pointing to the page, placed after the tool list
rather than at the end, so it does not overlap #1394.

Docs only; no code changes.
2026-09-20 19:21:49 +08:00
buyun14 899af80c2f fix(automation): consume and report the tail of a batch command line
`run_command_line` feeds the tokens after the command verb to the interactive
command and then terminates it as if Enter were pressed. Two gaps left a
headless caller unable to tell what actually happened.

In-place text editor takes over, remaining tokens were dropped
----------------------------------------------------------------
The `TEXT` content step hands over to the in-place editor, at which point the
command has already ended (`active_cmd` is `None`). The feed loop stopped
there, so the rest of the line — including the text itself — vanished:
`TEXT 0,0 5 0 hello` reported `completed` with `added: 0` and created nothing.
The tail is now typed into that editor and committed, and the commit is done
synchronously so its result is observable: `text_inline_commit()` returning
false leaves the tokens in `unconsumed` instead of reporting a successful line.
Dispatch the commit as a queued message instead and a failed commit would look
exactly like a good one — the text dropped, `added: 0`, `unconsumed: []`.

Only an editor this line opens is taken over, and after a successful commit the
line is closed with an escape: `TEXT` re-arms for the next line
(`open_next_line`), which would otherwise let the *next* batch line be eaten as
the next text position.

Leftover tokens are dropped
---------------------------
Any token no prompt ever asked for is recorded in `CommandLine::unconsumed` and
surfaced by the automation `run` response, so over-typed or misplaced input is
visible instead of silently ignored: `CIRCLE 0,0 5 9` reports
`"unconsumed": ["9"]` while a fully consumed line reports an empty list.

Pending surfaces are reported instead of `completed`
----------------------------------------------------
`run` now carries `blocked_by` — `"command"`, `"text_editor"`,
`"mtext_editor"`, `"modal:<Kind>"`, or `null` — and answers `waiting_input`
when something is still open rather than claiming `completed`.

`blocked_by` is attributed to the line that opened the surface: a condition and
its snapshot are taken before the command runs, so an editor left open by an
earlier line, another tab, or a startup dialog does not turn a finished command
into `waiting_input`. Without that attribution a caller polling for `completed`
would wait forever on someone else's editor. `docs/automation` documented
`waiting_input` as "more input is required", which is exactly what a pending
surface is; the two new fields are documented there as well.

Tests
-----
* `batch_text_line_creates_text_and_consumes_every_token` — the regression case
  creates exactly one `Text` entity and leaves nothing unconsumed.
* `multi_word_text_keeps_the_spaces` — asserts the committed string, so an
  off-by-one in the consumed index or a wrong join cannot pass.
* `run_reports_leftover_tokens_instead_of_dropping_them`.
* `text_without_content_reports_the_open_editor`.
* `a_stray_editor_is_not_hijacked_by_the_next_line` — the next line neither
  types into the stray editor nor inherits its blocker (`blocked_by: null`,
  `status: completed`).
* `a_command_still_waiting_is_named_as_the_blocker`.

Verified over the `--serve` protocol: `TEXT 0,0 5 0 hello world` → `completed`,
`added: 1`, and the entity really holds `hello world`; `TEXT 40,0 5 0` →
`waiting_input` + `blocked_by: "text_editor"`; the following `CIRCLE 0,0 5 9` →
`completed`, `blocked_by: null`, `unconsumed: ["9"]`; `LINE` → `waiting_input`
+ `blocked_by: "command"`.

Full lib suite: 1497 passed, 2 failed — both failures
(`io::plot_style::…named_tables_and_missing_files_get_a_reason`,
`scene::parametric_constraints::…arc_grips_drive_center_start_and_end_but_not_midpoint`)
reproduce on the unmodified tree; run with `LC_ALL=C` since several xref tests
otherwise fail on a non-English locale.

Known remaining gap, unchanged here: `MTEXT` / `HATCH` still answer `completed`
with `added: 0` in this path (they need interactive surfaces the feeder has no
tokens to fill). Reported separately in #1390.
2026-09-20 19:15:55 +08:00
Helen Kwok 2e871c2c6c docs(automation): document driving the web build
Describe the two JavaScript functions the web build exposes
(ocs_control_submit / ocs_control_take), how to poll them, the queue and reply
limits, opening a drawing from bytes with data_base64, and closing the startup
dialog before new. Everything is taken from src/app/control/mod.rs,
src/app/startup.rs and docs/automation/web_save_smoke.cjs.
2026-09-20 12:50:39 +09:30
Hakan Seven 4ddd19f6e8 feat(mcp): describe complete CAD record schemas 2026-09-09 01:01:44 +03:00
Hakan Seven 8f574c8388 feat(mcp): expose complete drawing records 2026-09-08 23:59:13 +03:00
Hakan Seven d35a93c6e8 Avoid reserved MCP session argument 2026-09-07 19:34:31 +03:00
Hakan Seven 2ebad14586 fix(web): capture save previews from the browser canvas
Capture the rendered canvas during the clean frame instead of requesting synchronous GPU buffer readback. Preserve embedded previews and allow saving when canvas readback is denied. Add a browser save and reopen regression check.

Fixes #1021
2026-09-06 13:14:56 +03:00
Hakan Seven cb88f2ded6 Accelerate native MCP workflows 2026-09-05 19:03:16 +03:00
Hakan Seven 0e4f755e6f Make native MCP self-describing 2026-09-05 18:22:05 +03:00
Hakan Seven 28d3aa5be2 Harden native MCP compatibility 2026-09-05 01:36:23 +03:00
Hakan Seven cbb4015760 Remove MCP bundle distribution 2026-09-05 01:10:13 +03:00
Hakan Seven afbf826b0c Ship client-neutral MCP bundles 2026-09-04 23:58:39 +03:00
Hakan Seven 071e224d26 Add client-neutral native MCP control 2026-09-04 23:27:03 +03:00
Hakan Seven 28cc473062 Add persistent live OpenCADStudio control 2026-09-04 22:25:55 +03:00
Hakan SevenandClaude Opus 4.8 8bae517eec feat(automation): local TCP socket transport for --serve
OpenCADStudio --serve --port <N> listens on 127.0.0.1:<N> and serves the
same line-based JSON protocol over the socket (one client at a time; the
document session persists across reconnects), as an alternative to
stdin/stdout. ocs.py grows a port= option to connect either way.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 01:54:03 +03:00
Hakan SevenandClaude Opus 4.8 d4f5ed3135 feat(automation): richer query geometry + layers/header introspection
query now emits geometry for Ellipse/Text/MText/Polyline/Insert (on top
of Line/Circle/Arc/Point). Add layers op (name, ACI/RGB colour, on/off,
frozen, locked + current layer) and header op (units, PDMODE/PDSIZE,
LTSCALE, annotation scale, current layer/text style). ocs.py gains
layers()/header(). Lets an agent read the drawing's structure and
settings, not just entity counts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 01:50:58 +03:00
Hakan SevenandClaude Opus 4.8 8d3d0c30cc feat(automation): undo/redo ops (move/copy already work via select+run)
Add undo and redo ops to the headless server (synchronous via update()).
Combined with select, the modify commands MOVE/COPY work through the
existing dispatcher — "select type=Line" then run("MOVE 0,0 10,0")
translates the selection. ocs.py gains undo()/redo().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 21:31:15 +03:00
Hakan SevenandClaude Opus 4.8 c78bc62056 feat(automation): select op — target entities by handle/type/layer
Add a select op that sets the document selection (by hex handles from
query, or by type/layer, or clear). Selection drives modify commands, so
an agent can query → select → run("ERASE") to act on specific entities.
Completes the modify half of the act/observe loop. ocs.py gains select().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 21:27:26 +03:00
Hakan SevenandClaude Opus 4.8 6d29b8458c feat(automation): query op — list entities with detail and filters
Add a query op to the headless server: list entities with handle, type,
layer and basic geometry (Line start/end, Circle/Arc centre+radius, Point
location), filtered by type and/or layer and capped by limit. Closes the
observe half of an agent's act/observe loop. ocs.py gains ocs.query().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 21:22:46 +03:00
Hakan SevenandClaude Opus 4.8 f1a59b1094 feat(automation): drive draw commands headless with coordinate args
run now feeds coordinate tokens to interactive tools: "LINE 0,0 10,10"
starts the tool, feeds the points, and terminates as if Enter were
pressed — creating real geometry through the existing command system. The
headless session also leaves the welcome (Start) tab so drawing commands
aren'\''t blocked. Verified end-to-end: LINE/CIRCLE create entities that
save and reopen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 21:17:17 +03:00
Hakan SevenandClaude Opus 4.8 5af1c9d841 chore: ignore Python __pycache__ (stray automation client cache)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 21:05:30 +03:00
Hakan SevenandClaude Opus 4.8 7f10b69e3c feat(automation): headless JSON server (--serve) + Python client
Add an external automation API (issue #29 / #100 track 2): `OpenCADStudio
--serve` runs without a GUI and is driven over a line-based JSON protocol
on stdin/stdout — open / new / run / entities / save. State persists
across requests so a script or AI agent can act, observe, and act again.

`run` drives the app's existing command system rather than a separate
binding, so coverage grows with the app; synchronous commands apply now,
pick-based interactive ones come once coordinate feeding is wired. Ships
a ~100-line example ocs.py client over the same protocol — no FFI to
maintain. Headless works because the app already constructs and dispatches
GUI-less in tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 21:05:12 +03:00