vortex-cli 6.0.0__tar.gz → 6.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. {vortex_cli-6.0.0/vortex_cli.egg-info → vortex_cli-6.1.0}/PKG-INFO +76 -13
  2. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/README.md +75 -12
  3. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/pyproject.toml +1 -1
  4. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/cli.py +22 -6
  5. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/clean.py +14 -13
  6. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/clone.py +73 -49
  7. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/compile.py +7 -2
  8. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/config.py +1 -2
  9. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/copy.py +29 -4
  10. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/db.py +99 -11
  11. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/delete.py +24 -10
  12. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/export.py +16 -4
  13. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/new.py +51 -17
  14. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/pull.py +1 -1
  15. vortex_cli-6.1.0/vortex/commands/schema.py +669 -0
  16. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/watch.py +44 -7
  17. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/gateway.py +64 -0
  18. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/main.py +35 -1
  19. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/templates/agent/skills/vortex-workflow/SKILL.md +15 -11
  20. vortex_cli-6.1.0/vortex/webdesign.py +653 -0
  21. {vortex_cli-6.0.0 → vortex_cli-6.1.0/vortex_cli.egg-info}/PKG-INFO +76 -13
  22. vortex_cli-6.0.0/vortex/commands/schema.py +0 -400
  23. vortex_cli-6.0.0/vortex/webdesign.py +0 -120
  24. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/LICENSE +0 -0
  25. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/setup.cfg +0 -0
  26. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/__init__.py +0 -0
  27. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/__main__.py +0 -0
  28. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/colour.py +0 -0
  29. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/__init__.py +0 -0
  30. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/agenda.py +0 -0
  31. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/agent.py +0 -0
  32. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/code.py +0 -0
  33. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/docs.py +0 -0
  34. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/execute.py +0 -0
  35. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/find.py +0 -0
  36. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/grep.py +0 -0
  37. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/import_.py +0 -0
  38. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/libs.py +0 -0
  39. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/list.py +0 -0
  40. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/log.py +0 -0
  41. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/push.py +0 -0
  42. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/render.py +0 -0
  43. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/status.py +0 -0
  44. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/undo.py +0 -0
  45. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/commands/use.py +0 -0
  46. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/constants.py +0 -0
  47. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/docs/Blackbook v2.md +0 -0
  48. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/docs/Blackbook.pdf +0 -0
  49. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/docs/index.html +0 -0
  50. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/docs/marked.min.js +0 -0
  51. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/lib/puakma-6.0.40.jar +0 -0
  52. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/libs.py +0 -0
  53. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/logging.py +0 -0
  54. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/models.py +0 -0
  55. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/soap.py +0 -0
  56. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/spinner.py +0 -0
  57. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/templates/agent/AGENTS.md +0 -0
  58. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/templates/agent/skills/puakma-database/SKILL.md +0 -0
  59. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/templates/agent/skills/puakma-design-elements/SKILL.md +0 -0
  60. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/templates/agent/skills/puakma-overview/SKILL.md +0 -0
  61. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/templates/agent/vortex.code-snippets +0 -0
  62. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/util.py +0 -0
  63. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex/workspace.py +0 -0
  64. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex_cli.egg-info/SOURCES.txt +0 -0
  65. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex_cli.egg-info/dependency_links.txt +0 -0
  66. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex_cli.egg-info/entry_points.txt +0 -0
  67. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex_cli.egg-info/requires.txt +0 -0
  68. {vortex_cli-6.0.0 → vortex_cli-6.1.0}/vortex_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vortex_cli
3
- Version: 6.0.0
3
+ Version: 6.1.0
4
4
  Summary: Vortex CLI
5
5
  Author-email: Jordan Amos <jordan.amos@gmail.com>
6
6
  License: MIT License
@@ -150,15 +150,15 @@ For a full list of commands see `--help`.
150
150
  - `code`: Open the workspace in Visual Studio Code (`-s <server>` opens that server's own workspace with exactly its jars on the Java classpath).
151
151
  - `use`: Set the default server so you don't need to pass `--server` on every command. e.g. `vortex use production`
152
152
  - `list` (or `ls`): List Puakma Applications on the server or cloned locally. (`ls` is an alias for `vortex list --local`)
153
- - `clone`: Clone Puakma Applications and their design objects into the workspace. Apps can be referenced by ID (`vortex clone 13`), by TemplateName (`vortex clone bettrackr_app`), or by group/name (`vortex clone bettrackr/app`). A bare application group clones **every** application in it (`vortex clone BetTrackr`) - all optionally server-qualified (`dev:13`, `dev:bettrackr/app`, `dev:BetTrackr`). See [Cloning a whole group](#cloning-a-whole-group).
153
+ - `clone`: Clone Puakma Applications and their design objects into the workspace. Apps can be referenced by ID (`vortex clone 13`), by TemplateName (`vortex clone bettrackr_app`), or by group/name (`vortex clone bettrackr/app`). A bare application group clones every active, non-inherited application in it (`vortex clone BetTrackr`; add `--all`/`-a` for the disabled and inherited ones too) - all optionally server-qualified (`dev:13`, `dev:bettrackr/app`, `dev:BetTrackr`). `--reclone` re-clones what is already cloned: every server's clones, or one server's with `--server`. See [Cloning a whole group](#cloning-a-whole-group).
154
154
  - `watch`: Watch the workspace for changes to Design Objects and automatically upload them to the server each app was cloned from. Watches all servers at once unless `--server` is given.
155
155
  - `clean`: Delete the locally cloned Puakma Application directories in the workspace.
156
156
  Takes optional `APP_ID`s to clean just those clones (all of the server's, if none are
157
- given). Refuses to delete a clone holding local changes the server doesn't have -
158
- naming each file - unless `--force` is given. Verifying that asks the server for its
159
- design element hashes, so a non-forced `clean` needs a reachable `backend=gateway`
160
- server; a clone it can't verify is refused, not silently deleted (`--force` needs no
161
- network).
157
+ given). Deletes immediately and needs no network - there is no undo. With `--check`
158
+ it first asks the server for its design element hashes and refuses to delete a clone
159
+ holding local changes the server doesn't have, naming each file; that needs a
160
+ reachable `backend=gateway` server, and a clone it can't verify is refused rather
161
+ than silently deleted.
162
162
  - `config`: View and manage configuration. `--check-gateway` reports the agent gateway negotiation and which gateway roles your identity holds.
163
163
  - `log`: View the server log.
164
164
  - `status`: Show the server status - via the agent gateway when available, otherwise the raw console `status` output.
@@ -188,11 +188,16 @@ For a full list of commands see `--help`.
188
188
  vortex clone 13 # by ID
189
189
  vortex clone bettrackr_app # by TemplateName
190
190
  vortex clone bettrackr/app # by group/name
191
- vortex clone BetTrackr # every application in the BetTrackr group
191
+ vortex clone BetTrackr # every active application in the BetTrackr group
192
+ vortex clone -a BetTrackr # ... including its disabled and inherited ones
192
193
  vortex clone dev:BetTrackr # ... on the 'dev' server
193
194
  vortex clone --group BetTrackr # explicitly a group, never a TemplateName
194
195
  ```
195
196
 
197
+ A group clone skips disabled and inherited applications (the ones `vortex list`
198
+ hides by default) unless `--all`/`-a` is given. An application you name outright -
199
+ by ID, TemplateName or `group/name` - is always cloned, whatever its state.
200
+
196
201
  Groups are matched the way `vortex list --group` matches them - a
197
202
  case-insensitive substring - except that an exact (case-insensitive) group name
198
203
  always wins, so cloning `BetTrackr` never drags in `BetTrackrLegacy`. If a
@@ -202,8 +207,8 @@ than cloning the lot.
202
207
  A bare word is looked up as **both** a TemplateName and a group. In the rare
203
208
  case that it is genuinely both, vortex refuses to guess: use `--group NAME` for
204
209
  the group, or the ID / `group/name` for the single application. Every flag
205
- (`--reclone`, `--get-resources`, `--open-urls`, `--timeout`, `--server`) applies
206
- to group clones as it does to single apps.
210
+ (`--reclone`, `--all`, `--get-resources`, `--open-urls`, `--timeout`, `--server`)
211
+ applies to group clones as it does to single apps.
207
212
 
208
213
  ### Working with Multiple Servers
209
214
 
@@ -303,7 +308,10 @@ backend = gateway ; default is 'soap', which never contacts the gatew
303
308
 
304
309
  With `backend = gateway`, `watch`/`push` uploads, `delete`, `log`, `status`, `db`, `execute`
305
310
  and the journal commands route through it, and `agenda`, `pull`, `render` and `undo` become
306
- available. Check what you are talking to and what you may do:
311
+ available. Everything the gateway does not offer goes to the **webdesign vortex API** (the
312
+ JSON action served by `system/webdesign`) - see [SOAP-free on backend = gateway](#soap-free-on-backend--gateway).
313
+ A `backend = gateway` server never talks to SOAPDesigner at all. Check what you are talking
314
+ to and what you may do:
307
315
 
308
316
  ```
309
317
  vortex config --check-gateway -s dev
@@ -313,8 +321,9 @@ vortex config --check-gateway -s dev
313
321
  identity lacks the required role) is reported as an error - vortex never quietly retries the
314
322
  same operation over SOAP, because that would let anyone with SOAP access bypass every role,
315
323
  journal and guardrail above. Likewise `backend = soap` never contacts the gateway. The one
316
- deliberate exception is the gateway application's *own* deployments, which always use SOAP so
317
- that a broken gateway deploy never needs the gateway to fix itself.
324
+ deliberate exception is the gateway application's *own* deployments, which never go through
325
+ the gateway (webdesign on `backend = gateway`, SOAP on `backend = soap`) so that a broken
326
+ gateway deploy never needs the gateway to fix itself.
318
327
 
319
328
  The gateway application is **not bundled with this CLI** - deploy it to a server with
320
329
  `vortex export` / `vortex import`, then run its `Setup` scheduled action. It ships its own
@@ -326,6 +335,55 @@ the running server documents its own endpoints, roles and setup steps.
326
335
  > grant itself any role. Keep webdesign access for operators; agent identities should not have
327
336
  > it.
328
337
 
338
+ ### SOAP-free on `backend = gateway`
339
+
340
+ SOAPDesigner is legacy. On a `backend = gateway` server the commands the gateway does not
341
+ cover use the JSON API of `system/webdesign`'s `vortex` action instead - with the same
342
+ credentials, and the webdesign app's own ACL. SOAP is used only when a server is explicitly
343
+ `backend = soap`. The data dictionary (`schema`, `db --list/--schema`) is the exception that
344
+ proves the rule: the gateway's `dictionary` endpoint runs that same webdesign `vortex` action
345
+ server-side behind `GatewayDBRead`/`GatewayDBWrite`, so an agent identity with no webdesign
346
+ access gets a clean role answer rather than a login page, and a change to `vortex.java`
347
+ needs no change to the gateway.
348
+
349
+ | Command | `backend = gateway` | `backend = soap` |
350
+ |---|---|---|
351
+ | `push`, `watch` (modify), `compile --upload` | gateway `upload` (journalled); the gateway app itself: webdesign `PUT design` | SOAP `uploadDesign` |
352
+ | `watch` (create / delete) | webdesign `POST` / `DELETE design` | SOAP |
353
+ | `delete` | gateway `delete` (journalled); the gateway app itself: webdesign | SOAP |
354
+ | `copy`, `new object` | webdesign `POST`/`PUT design`, `PUT design/params` | SOAP |
355
+ | `new app` | webdesign `POST vortex` | SOAP `saveApplication` |
356
+ | `new keyword` | gateway `keyword` (upsert) | SOAP `saveKeyword` |
357
+ | `schema`, `db --list`, `db --schema` | gateway `dictionary` (webdesign's `database`/`table`/`column` routes run server-side; `GatewayDBRead`/`GatewayDBWrite`) | SOAP SQL + `savePuakma*` |
358
+ | `export` | webdesign `ExportPMX` action | SOAP `downloadPmx` |
359
+ | `import` | **still SOAP** - webdesign has no import route yet | SOAP `uploadPmx` |
360
+ | `db --sql`, `list`, `log`, `execute`, `status`, `clone`, `pull` | gateway | SOAP |
361
+
362
+ Where the webdesign API behaves differently from SOAP, the CLI compensates so the commands
363
+ behave as they did. The differences that remain visible:
364
+
365
+ - **Design element writes are whole-row.** The CLI reads the row back first and re-sends what
366
+ it does not model (the other blob, `Options`), so a `DATA`-only upload never wipes source.
367
+ One consequence: every write is one extra `GET`.
368
+ - **Renaming with `new object --update --name` does not rewrite references.** SOAP's
369
+ `updateDesignObject` also updated design params in the app that referred to the old name;
370
+ the webdesign route does not. Fix `OpenAction`/`ParentPage`-style params by hand after a rename.
371
+ - **`schema` cannot record `--default`, `--position` or the column half of `--ref`.** The
372
+ webdesign column write has no `DefaultValue`/`Position`/`RefColumn` fields (SOAP's
373
+ `savePuakmaAttribute2` had them). `--default`/`--position` are refused with a message;
374
+ `--ref TABLE.COLUMN` records the table and warns. Updates leave existing values untouched.
375
+ Extend `POST`/`PUT .../column` in webdesign's `vortex.java` to lift this.
376
+ - **Every write flushes the application's design cache**, where SOAP flushed one element.
377
+ - **No per-application `Developer` role check** - the webdesign app's ACL is the boundary. An
378
+ identity that can reach `system/webdesign` can edit every application through it.
379
+ - **`db --list` orders by table name** (SOAP's `SELECT DISTINCT` had no order); `--schema`
380
+ output is identical.
381
+ - **Database-name resolution is app-scoped.** `schema`/`db --list` find the connection whose
382
+ dictionary holds the tables (as SOAP did by `pmatable` count), asking locally cloned
383
+ applications first and scanning the server inventory only if none of them has it.
384
+ - **`import` remains SOAP** on every backend until webdesign gains a PMX import route
385
+ (`SaveImportPMX` is a multipart UI form, not an API).
386
+
329
387
  ### Interactive Wizards
330
388
 
331
389
  Run `vortex new object` or `vortex new app` without any flags to launch a step-by-step wizard:
@@ -421,6 +479,11 @@ vortex schema mydb --add-column invoice customer_id --type BIGINT --ref customer
421
479
  vortex schema mydb --ddl invoice # print CREATE TABLE from the dictionary
422
480
  ```
423
481
 
482
+ On a `backend = gateway` server the dictionary is reached through webdesign's vortex API via
483
+ the gateway's `dictionary` endpoint (`GatewayDBRead` to read, `GatewayDBWrite` to change).
484
+ That API cannot record `--default` or `--position` (refused) nor the column half of `--ref`
485
+ (warned) - see [SOAP-free on backend = gateway](#soap-free-on-backend--gateway).
486
+
424
487
  ### Agent Support Files
425
488
 
426
489
  `vortex agent` copies bundled support files into the workspace `.vscode` directory so they are
@@ -106,15 +106,15 @@ For a full list of commands see `--help`.
106
106
  - `code`: Open the workspace in Visual Studio Code (`-s <server>` opens that server's own workspace with exactly its jars on the Java classpath).
107
107
  - `use`: Set the default server so you don't need to pass `--server` on every command. e.g. `vortex use production`
108
108
  - `list` (or `ls`): List Puakma Applications on the server or cloned locally. (`ls` is an alias for `vortex list --local`)
109
- - `clone`: Clone Puakma Applications and their design objects into the workspace. Apps can be referenced by ID (`vortex clone 13`), by TemplateName (`vortex clone bettrackr_app`), or by group/name (`vortex clone bettrackr/app`). A bare application group clones **every** application in it (`vortex clone BetTrackr`) - all optionally server-qualified (`dev:13`, `dev:bettrackr/app`, `dev:BetTrackr`). See [Cloning a whole group](#cloning-a-whole-group).
109
+ - `clone`: Clone Puakma Applications and their design objects into the workspace. Apps can be referenced by ID (`vortex clone 13`), by TemplateName (`vortex clone bettrackr_app`), or by group/name (`vortex clone bettrackr/app`). A bare application group clones every active, non-inherited application in it (`vortex clone BetTrackr`; add `--all`/`-a` for the disabled and inherited ones too) - all optionally server-qualified (`dev:13`, `dev:bettrackr/app`, `dev:BetTrackr`). `--reclone` re-clones what is already cloned: every server's clones, or one server's with `--server`. See [Cloning a whole group](#cloning-a-whole-group).
110
110
  - `watch`: Watch the workspace for changes to Design Objects and automatically upload them to the server each app was cloned from. Watches all servers at once unless `--server` is given.
111
111
  - `clean`: Delete the locally cloned Puakma Application directories in the workspace.
112
112
  Takes optional `APP_ID`s to clean just those clones (all of the server's, if none are
113
- given). Refuses to delete a clone holding local changes the server doesn't have -
114
- naming each file - unless `--force` is given. Verifying that asks the server for its
115
- design element hashes, so a non-forced `clean` needs a reachable `backend=gateway`
116
- server; a clone it can't verify is refused, not silently deleted (`--force` needs no
117
- network).
113
+ given). Deletes immediately and needs no network - there is no undo. With `--check`
114
+ it first asks the server for its design element hashes and refuses to delete a clone
115
+ holding local changes the server doesn't have, naming each file; that needs a
116
+ reachable `backend=gateway` server, and a clone it can't verify is refused rather
117
+ than silently deleted.
118
118
  - `config`: View and manage configuration. `--check-gateway` reports the agent gateway negotiation and which gateway roles your identity holds.
119
119
  - `log`: View the server log.
120
120
  - `status`: Show the server status - via the agent gateway when available, otherwise the raw console `status` output.
@@ -144,11 +144,16 @@ For a full list of commands see `--help`.
144
144
  vortex clone 13 # by ID
145
145
  vortex clone bettrackr_app # by TemplateName
146
146
  vortex clone bettrackr/app # by group/name
147
- vortex clone BetTrackr # every application in the BetTrackr group
147
+ vortex clone BetTrackr # every active application in the BetTrackr group
148
+ vortex clone -a BetTrackr # ... including its disabled and inherited ones
148
149
  vortex clone dev:BetTrackr # ... on the 'dev' server
149
150
  vortex clone --group BetTrackr # explicitly a group, never a TemplateName
150
151
  ```
151
152
 
153
+ A group clone skips disabled and inherited applications (the ones `vortex list`
154
+ hides by default) unless `--all`/`-a` is given. An application you name outright -
155
+ by ID, TemplateName or `group/name` - is always cloned, whatever its state.
156
+
152
157
  Groups are matched the way `vortex list --group` matches them - a
153
158
  case-insensitive substring - except that an exact (case-insensitive) group name
154
159
  always wins, so cloning `BetTrackr` never drags in `BetTrackrLegacy`. If a
@@ -158,8 +163,8 @@ than cloning the lot.
158
163
  A bare word is looked up as **both** a TemplateName and a group. In the rare
159
164
  case that it is genuinely both, vortex refuses to guess: use `--group NAME` for
160
165
  the group, or the ID / `group/name` for the single application. Every flag
161
- (`--reclone`, `--get-resources`, `--open-urls`, `--timeout`, `--server`) applies
162
- to group clones as it does to single apps.
166
+ (`--reclone`, `--all`, `--get-resources`, `--open-urls`, `--timeout`, `--server`)
167
+ applies to group clones as it does to single apps.
163
168
 
164
169
  ### Working with Multiple Servers
165
170
 
@@ -259,7 +264,10 @@ backend = gateway ; default is 'soap', which never contacts the gatew
259
264
 
260
265
  With `backend = gateway`, `watch`/`push` uploads, `delete`, `log`, `status`, `db`, `execute`
261
266
  and the journal commands route through it, and `agenda`, `pull`, `render` and `undo` become
262
- available. Check what you are talking to and what you may do:
267
+ available. Everything the gateway does not offer goes to the **webdesign vortex API** (the
268
+ JSON action served by `system/webdesign`) - see [SOAP-free on backend = gateway](#soap-free-on-backend--gateway).
269
+ A `backend = gateway` server never talks to SOAPDesigner at all. Check what you are talking
270
+ to and what you may do:
263
271
 
264
272
  ```
265
273
  vortex config --check-gateway -s dev
@@ -269,8 +277,9 @@ vortex config --check-gateway -s dev
269
277
  identity lacks the required role) is reported as an error - vortex never quietly retries the
270
278
  same operation over SOAP, because that would let anyone with SOAP access bypass every role,
271
279
  journal and guardrail above. Likewise `backend = soap` never contacts the gateway. The one
272
- deliberate exception is the gateway application's *own* deployments, which always use SOAP so
273
- that a broken gateway deploy never needs the gateway to fix itself.
280
+ deliberate exception is the gateway application's *own* deployments, which never go through
281
+ the gateway (webdesign on `backend = gateway`, SOAP on `backend = soap`) so that a broken
282
+ gateway deploy never needs the gateway to fix itself.
274
283
 
275
284
  The gateway application is **not bundled with this CLI** - deploy it to a server with
276
285
  `vortex export` / `vortex import`, then run its `Setup` scheduled action. It ships its own
@@ -282,6 +291,55 @@ the running server documents its own endpoints, roles and setup steps.
282
291
  > grant itself any role. Keep webdesign access for operators; agent identities should not have
283
292
  > it.
284
293
 
294
+ ### SOAP-free on `backend = gateway`
295
+
296
+ SOAPDesigner is legacy. On a `backend = gateway` server the commands the gateway does not
297
+ cover use the JSON API of `system/webdesign`'s `vortex` action instead - with the same
298
+ credentials, and the webdesign app's own ACL. SOAP is used only when a server is explicitly
299
+ `backend = soap`. The data dictionary (`schema`, `db --list/--schema`) is the exception that
300
+ proves the rule: the gateway's `dictionary` endpoint runs that same webdesign `vortex` action
301
+ server-side behind `GatewayDBRead`/`GatewayDBWrite`, so an agent identity with no webdesign
302
+ access gets a clean role answer rather than a login page, and a change to `vortex.java`
303
+ needs no change to the gateway.
304
+
305
+ | Command | `backend = gateway` | `backend = soap` |
306
+ |---|---|---|
307
+ | `push`, `watch` (modify), `compile --upload` | gateway `upload` (journalled); the gateway app itself: webdesign `PUT design` | SOAP `uploadDesign` |
308
+ | `watch` (create / delete) | webdesign `POST` / `DELETE design` | SOAP |
309
+ | `delete` | gateway `delete` (journalled); the gateway app itself: webdesign | SOAP |
310
+ | `copy`, `new object` | webdesign `POST`/`PUT design`, `PUT design/params` | SOAP |
311
+ | `new app` | webdesign `POST vortex` | SOAP `saveApplication` |
312
+ | `new keyword` | gateway `keyword` (upsert) | SOAP `saveKeyword` |
313
+ | `schema`, `db --list`, `db --schema` | gateway `dictionary` (webdesign's `database`/`table`/`column` routes run server-side; `GatewayDBRead`/`GatewayDBWrite`) | SOAP SQL + `savePuakma*` |
314
+ | `export` | webdesign `ExportPMX` action | SOAP `downloadPmx` |
315
+ | `import` | **still SOAP** - webdesign has no import route yet | SOAP `uploadPmx` |
316
+ | `db --sql`, `list`, `log`, `execute`, `status`, `clone`, `pull` | gateway | SOAP |
317
+
318
+ Where the webdesign API behaves differently from SOAP, the CLI compensates so the commands
319
+ behave as they did. The differences that remain visible:
320
+
321
+ - **Design element writes are whole-row.** The CLI reads the row back first and re-sends what
322
+ it does not model (the other blob, `Options`), so a `DATA`-only upload never wipes source.
323
+ One consequence: every write is one extra `GET`.
324
+ - **Renaming with `new object --update --name` does not rewrite references.** SOAP's
325
+ `updateDesignObject` also updated design params in the app that referred to the old name;
326
+ the webdesign route does not. Fix `OpenAction`/`ParentPage`-style params by hand after a rename.
327
+ - **`schema` cannot record `--default`, `--position` or the column half of `--ref`.** The
328
+ webdesign column write has no `DefaultValue`/`Position`/`RefColumn` fields (SOAP's
329
+ `savePuakmaAttribute2` had them). `--default`/`--position` are refused with a message;
330
+ `--ref TABLE.COLUMN` records the table and warns. Updates leave existing values untouched.
331
+ Extend `POST`/`PUT .../column` in webdesign's `vortex.java` to lift this.
332
+ - **Every write flushes the application's design cache**, where SOAP flushed one element.
333
+ - **No per-application `Developer` role check** - the webdesign app's ACL is the boundary. An
334
+ identity that can reach `system/webdesign` can edit every application through it.
335
+ - **`db --list` orders by table name** (SOAP's `SELECT DISTINCT` had no order); `--schema`
336
+ output is identical.
337
+ - **Database-name resolution is app-scoped.** `schema`/`db --list` find the connection whose
338
+ dictionary holds the tables (as SOAP did by `pmatable` count), asking locally cloned
339
+ applications first and scanning the server inventory only if none of them has it.
340
+ - **`import` remains SOAP** on every backend until webdesign gains a PMX import route
341
+ (`SaveImportPMX` is a multipart UI form, not an API).
342
+
285
343
  ### Interactive Wizards
286
344
 
287
345
  Run `vortex new object` or `vortex new app` without any flags to launch a step-by-step wizard:
@@ -377,6 +435,11 @@ vortex schema mydb --add-column invoice customer_id --type BIGINT --ref customer
377
435
  vortex schema mydb --ddl invoice # print CREATE TABLE from the dictionary
378
436
  ```
379
437
 
438
+ On a `backend = gateway` server the dictionary is reached through webdesign's vortex API via
439
+ the gateway's `dictionary` endpoint (`GatewayDBRead` to read, `GatewayDBWrite` to change).
440
+ That API cannot record `--default` or `--position` (refused) nor the column half of `--ref`
441
+ (warned) - see [SOAP-free on backend = gateway](#soap-free-on-backend--gateway).
442
+
380
443
  ### Agent Support Files
381
444
 
382
445
  `vortex agent` copies bundled support files into the workspace `.vscode` directory so they are
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "vortex_cli"
8
- version = "6.0.0"
8
+ version = "6.1.0"
9
9
  description = "Vortex CLI"
10
10
  requires-python = ">=3.10"
11
11
  readme = { file = "README.md", content-type = "text/markdown" }
@@ -410,8 +410,22 @@ def add_clone_parser(
410
410
  )
411
411
  clone_parser.add_argument(
412
412
  "--reclone",
413
- help="Reclone locally cloned applications",
413
+ help=(
414
+ "Reclone the locally cloned applications: every server's "
415
+ "clones, or only those of the given --server"
416
+ ),
417
+ action="store_true",
418
+ )
419
+ clone_parser.add_argument(
420
+ "--all",
421
+ "-a",
422
+ dest="include_all",
414
423
  action="store_true",
424
+ help=(
425
+ "When cloning a group, also clone its disabled and inherited "
426
+ "applications (skipped by default). An application named "
427
+ "outright is always cloned"
428
+ ),
415
429
  )
416
430
  clone_parser.add_argument(
417
431
  "--get-resources",
@@ -504,8 +518,8 @@ def add_clean_parser(command_parser: _SubParsersAction[ArgumentParser]) -> None:
504
518
  "clean",
505
519
  help=(
506
520
  "Delete the cloned Puakma Application directories in the "
507
- "workspace. Refuses to delete a clone holding local changes "
508
- "the server doesn't have, without --force"
521
+ "workspace. There is no undo; --check first asks the server "
522
+ "whether a clone holds local changes it doesn't have"
509
523
  ),
510
524
  )
511
525
  clean_parser.add_argument(
@@ -525,11 +539,13 @@ def add_clean_parser(command_parser: _SubParsersAction[ArgumentParser]) -> None:
525
539
  action="store_true",
526
540
  )
527
541
  clean_parser.add_argument(
528
- "--force",
542
+ "--check",
529
543
  action="store_true",
530
544
  help=(
531
- "Delete even clones with local changes the server doesn't "
532
- "have (or that can't be verified against it). There is no undo"
545
+ "Before deleting, verify each clone against the server's design "
546
+ "element hashes and refuse any holding local changes the server "
547
+ "doesn't have (or that can't be verified). Needs a reachable "
548
+ "backend=gateway server"
533
549
  ),
534
550
  )
535
551
  clean_parser.add_argument(
@@ -9,20 +9,21 @@ the same 'app_ids: list[int] | None' convention 'vortex pull' and
9
9
  'vortex push' use, APPLICATION ids, not the design-element ids
10
10
  'vortex delete' takes - narrows it to those clones.
11
11
 
12
- THE SAFETY GATE. There is no undo and no git inside an app folder, so
13
- clean refuses to delete a clone holding local work the server does not
14
- have, naming each file, unless --force is given. Deciding that is the
12
+ THE OPT-IN SAFETY GATE. There is no undo and no git inside an app
13
+ folder. With --check, clean refuses to delete a clone holding local
14
+ work the server does not have, naming each file. Deciding that is the
15
15
  same three-way comparison pull makes (see pull.local_modifications)
16
16
  and it CANNOT be done offline: 'vortex push' does not move the
17
17
  manifest baseline, so after a push the local file differs from the
18
18
  baseline while matching the server. Telling 'already pushed, safe'
19
- from 'unsaved work' needs the server's hashes, which makes a
20
- non-forced clean a network operation. When the server can't answer -
21
- unreachable, refusing, or backend=soap, which has no hash endpoint at
22
- all - the clone is UNVERIFIABLE and clean refuses it rather than
23
- silently skipping the gate exactly when it matters; --force is the
24
- escape hatch (as is 'vortex clean --force' for an offline machine).
25
- --force needs no network at all.
19
+ from 'unsaved work' needs the server's hashes, which makes --check a
20
+ network operation - one round trip per clone, and slow on a workspace
21
+ with many. That is why it is opt-in: a plain 'vortex clean' deletes
22
+ without asking the server anything (and so works offline). When
23
+ --check is given and the server can't answer - unreachable, refusing,
24
+ or backend=soap, which has no hash endpoint at all - the clone is
25
+ UNVERIFIABLE and clean refuses it rather than silently skipping the
26
+ gate exactly when it was asked for.
26
27
  """
27
28
 
28
29
  from __future__ import annotations
@@ -100,7 +101,7 @@ def _apply_gate(
100
101
  logger.error(
101
102
  f"{len(blocked)} application(s) hold unsaved local work (or could "
102
103
  "not be verified against the server) - push or pull them, or "
103
- "re-run with --force to discard them. There is no undo"
104
+ "re-run without --check to discard them. There is no undo"
104
105
  )
105
106
  return blocked
106
107
 
@@ -112,7 +113,7 @@ def clean(
112
113
  include_libs: bool = False,
113
114
  app_ids: list[int] | None = None,
114
115
  *,
115
- force: bool = False,
116
+ check: bool = False,
116
117
  ) -> int:
117
118
  cloned_apps = workspace.listapps(None if include_all else server)
118
119
 
@@ -124,7 +125,7 @@ def clean(
124
125
  return 1
125
126
  cloned_apps = [app for app in cloned_apps if app.id in wanted]
126
127
 
127
- if not force and cloned_apps:
128
+ if check and cloned_apps:
128
129
  if _apply_gate(workspace, cloned_apps):
129
130
  return 1
130
131
 
@@ -421,40 +421,28 @@ def _fetch_apps(
421
421
  group_filter: list[str],
422
422
  template_filter: list[str],
423
423
  strict_search: bool,
424
+ include_all: bool = True,
424
425
  ) -> list[PuakmaApplication]:
425
426
  """
426
- Applications matching the filters, inherited and inactive included -
427
- via the gateway when negotiated (client-side filtering, mirroring
428
- the list command), otherwise the SOAP query. One matching semantics
429
- for clone reference resolution on either backend.
427
+ Applications matching the filters - via the gateway when negotiated
428
+ (client-side filtering), otherwise the SOAP query - with exactly the
429
+ matching semantics of the list command on either backend. Inherited
430
+ and disabled applications are included only with include_all: an
431
+ explicitly named application is always looked up in full (the user
432
+ named it), a group expansion skips them unless 'clone --all'.
430
433
  """
431
- client = server.negotiate_gateway()
432
- if client is None:
433
- return server.fetch_all_apps(
434
- name_filter, group_filter, template_filter, strict_search, True, True
435
- )
436
- from vortex.commands.list import _filter_apps
437
-
438
- try:
439
- gateway_apps = client.get_apps()
440
- except GatewayRefused as e:
441
- raise GatewayUnavailable(
442
- f"Gateway refused listing applications ({e.error_code}): "
443
- "this identity needs GatewayDesignRead (or Admin)"
444
- ) from e
445
- apps = [
446
- PuakmaApplication(
447
- gw_app.id,
448
- gw_app.name,
449
- gw_app.group,
450
- gw_app.inherit_from or "",
451
- gw_app.template_name or "",
452
- server.host,
453
- server_name=server.name,
454
- )
455
- for gw_app in gateway_apps
456
- ]
457
- return _filter_apps(apps, name_filter, group_filter, template_filter, strict_search)
434
+ from vortex.commands.list import _fetch_apps as _list_fetch_apps
435
+
436
+ apps, _extras = _list_fetch_apps(
437
+ server,
438
+ name_filter,
439
+ group_filter,
440
+ template_filter,
441
+ strict_search,
442
+ include_all,
443
+ include_all,
444
+ )
445
+ return apps
458
446
 
459
447
 
460
448
  def _resolve_one(
@@ -476,29 +464,53 @@ def _resolve_one(
476
464
  return [app.id]
477
465
 
478
466
 
479
- def _fetch_group_apps(server: PuakmaServer, group: str) -> list[PuakmaApplication]:
467
+ def _fetch_group_apps(
468
+ server: PuakmaServer, group: str, include_all: bool = False
469
+ ) -> list[PuakmaApplication]:
480
470
  """
481
471
  Applications whose group matches `group`, matched the way
482
472
  'vortex list --group' matches groups - a case-insensitive substring.
483
473
  An exact (case-insensitive) group name always wins, so cloning
484
- 'BetTrackr' never drags in 'BetTrackrLegacy'. Like every other clone
485
- reference, inherited and inactive applications are included.
474
+ 'BetTrackr' never drags in 'BetTrackrLegacy'. Inherited and disabled
475
+ applications are left out unless include_all ('clone --all').
486
476
  """
487
- apps = _fetch_apps(server, [], [group], [], False)
477
+ apps = _fetch_apps(server, [], [group], [], False, include_all)
488
478
  exact = [app for app in apps if (app.group or "").casefold() == group.casefold()]
489
479
  return exact or apps
490
480
 
491
481
 
482
+ def _group_has_only_hidden_apps(
483
+ server: PuakmaServer, group: str, include_all: bool
484
+ ) -> bool:
485
+ """
486
+ True when a group matched nothing only because every application in
487
+ it is disabled or inherited - the case '--all' exists for. Logs why.
488
+ """
489
+ if include_all or not _fetch_group_apps(server, group, True):
490
+ return False
491
+ logger.error(
492
+ f"Every application in a group matching '{group}' on '{server.name}' "
493
+ "is disabled or inherited. Use '--all' to clone them too"
494
+ )
495
+ return True
496
+
497
+
492
498
  def _resolve_group(
493
- server: PuakmaServer, group: str, apps: list[PuakmaApplication] | None = None
499
+ server: PuakmaServer,
500
+ group: str,
501
+ apps: list[PuakmaApplication] | None = None,
502
+ *,
503
+ include_all: bool = False,
494
504
  ) -> list[int] | None:
495
505
  """Resolves a group reference to the ids of every application in it"""
496
506
  if apps is None:
497
- apps = _fetch_group_apps(server, group)
507
+ apps = _fetch_group_apps(server, group, include_all)
498
508
  if not apps:
499
- logger.error(
500
- f"No applications in a group matching '{group}' found on '{server.name}'"
501
- )
509
+ if not _group_has_only_hidden_apps(server, group, include_all):
510
+ logger.error(
511
+ f"No applications in a group matching '{group}' found on "
512
+ f"'{server.name}'"
513
+ )
502
514
  return None
503
515
  groups = sorted({app.group or "" for app in apps}, key=str.casefold)
504
516
  if len(groups) > 1:
@@ -513,15 +525,18 @@ def _resolve_group(
513
525
  return [app.id for app in apps]
514
526
 
515
527
 
516
- def _resolve_bare_word(server: PuakmaServer, word: str) -> list[int] | None:
528
+ def _resolve_bare_word(
529
+ server: PuakmaServer, word: str, include_all: bool = False
530
+ ) -> list[int] | None:
517
531
  """
518
532
  A bare word is either a TemplateName or an application group. Both
519
533
  are looked up and a word that is somehow both is an error rather
520
534
  than a coin toss - the ID or 'group/name' clones the application,
521
- '--group' clones the group.
535
+ '--group' clones the group. A TemplateName names one application,
536
+ so it is looked up in full; the group expansion honours include_all.
522
537
  """
523
538
  templates = _fetch_apps(server, [], [], [word], True)
524
- group_apps = _fetch_group_apps(server, word)
539
+ group_apps = _fetch_group_apps(server, word, include_all)
525
540
  if templates and group_apps:
526
541
  logger.error(
527
542
  f"'{word}' is both a TemplateName and an application group on "
@@ -530,15 +545,21 @@ def _resolve_bare_word(server: PuakmaServer, word: str) -> list[int] | None:
530
545
  )
531
546
  return None
532
547
  if group_apps:
533
- return _resolve_group(server, word, group_apps)
548
+ return _resolve_group(server, word, group_apps, include_all=include_all)
549
+ if not templates and _group_has_only_hidden_apps(server, word, include_all):
550
+ return None
534
551
  return _resolve_one(server, templates, f"TemplateName '{word}'")
535
552
 
536
553
 
537
- def _resolve_refs_to_ids(server: PuakmaServer, refs: list[AppRef]) -> list[int] | None:
554
+ def _resolve_refs_to_ids(
555
+ server: PuakmaServer, refs: list[AppRef], *, include_all: bool = False
556
+ ) -> list[int] | None:
538
557
  """
539
558
  Resolves TemplateName, group and group/name references to
540
559
  application ids by querying the server. Returns None (having logged
541
- an error) when a reference can't be resolved unambiguously.
560
+ an error) when a reference can't be resolved unambiguously. Group
561
+ references expand to their active, non-inherited applications
562
+ unless include_all; an application named outright always resolves.
542
563
  """
543
564
  ids: list[int] = []
544
565
  for ref in refs:
@@ -546,9 +567,9 @@ def _resolve_refs_to_ids(server: PuakmaServer, refs: list[AppRef]) -> list[int]
546
567
  ids.append(ref.id)
547
568
  continue
548
569
  if ref.is_group_only:
549
- resolved = _resolve_group(server, ref.group or "")
570
+ resolved = _resolve_group(server, ref.group or "", include_all=include_all)
550
571
  elif ref.template is not None:
551
- resolved = _resolve_bare_word(server, ref.template)
572
+ resolved = _resolve_bare_word(server, ref.template, include_all)
552
573
  else:
553
574
  matches = _fetch_apps(server, [ref.name or ""], [ref.group or ""], [], True)
554
575
  resolved = _resolve_one(
@@ -568,10 +589,13 @@ def clone(
568
589
  get_resources: bool = False,
569
590
  open_urls: bool = False,
570
591
  reclone: bool = False,
592
+ include_all: bool = False,
571
593
  timeout: int = 300,
572
594
  ) -> int:
573
595
  refs = list(app_refs)
574
596
  if reclone:
597
+ # Every application already cloned from THIS server. main()
598
+ # fans a bare '--reclone' out over every server with clones
575
599
  refs.extend(AppRef(None, id=app.id) for app in workspace.listapps(server))
576
600
 
577
601
  # Resolve credentials before the spinner starts so any interactive
@@ -580,7 +604,7 @@ def clone(
580
604
  # backend=gateway: negotiate now, before any locks - a gateway that
581
605
  # doesn't answer is an error here, never a silent SOAP fallback
582
606
  server.negotiate_gateway()
583
- app_ids = _resolve_refs_to_ids(server, refs)
607
+ app_ids = _resolve_refs_to_ids(server, refs, include_all=include_all)
584
608
  if app_ids is None:
585
609
  return 1
586
610
 
@@ -169,11 +169,16 @@ async def _aupload_classes(
169
169
  server: PuakmaServer,
170
170
  to_upload: list[tuple[PuakmaApplication, DesignObject]],
171
171
  ) -> int:
172
+ # The same routing as push: the gateway when negotiated (journalled,
173
+ # deploy-confirmed), the webdesign API for the gateway app itself,
174
+ # SOAP only on backend=soap - with the handshake lazy
175
+ from vortex.commands.watch import aupload_design_routed
176
+
172
177
  ret = 0
173
178
  async with server as s:
174
- await s.server_designer.ainitiate_connection()
179
+ await asyncio.to_thread(s.negotiate_gateway)
175
180
  for _app, obj in to_upload:
176
- ok = await obj.aupload(s.download_designer, upload_source=False)
181
+ ok = await aupload_design_routed(s, obj, False)
177
182
  ret |= 0 if ok else 1
178
183
  for app in {app for app, _obj in to_upload}:
179
184
  workspace.mkdir(app)