vortex-cli 4.20.0__tar.gz → 4.21.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.
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/PKG-INFO +48 -3
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/README.md +47 -2
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/pyproject.toml +10 -2
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/cli.py +126 -5
- vortex_cli-4.21.0/vortex/commands/agent.py +27 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/commands/clone.py +1 -1
- vortex_cli-4.21.0/vortex/commands/compile.py +208 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/commands/db.py +8 -3
- vortex_cli-4.21.0/vortex/commands/docs.py +82 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/commands/export.py +13 -9
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/commands/grep.py +5 -1
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/commands/import_.py +5 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/commands/list.py +1 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/commands/log.py +4 -4
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/commands/new.py +59 -23
- vortex_cli-4.21.0/vortex/commands/schema.py +394 -0
- vortex_cli-4.21.0/vortex/commands/use.py +65 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/commands/watch.py +11 -4
- vortex_cli-4.21.0/vortex/docs/Blackbook v2.md +2753 -0
- vortex_cli-4.21.0/vortex/docs/index.html +341 -0
- vortex_cli-4.21.0/vortex/docs/marked.min.js +6 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/main.py +49 -2
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/models.py +7 -5
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/soap.py +151 -5
- vortex_cli-4.21.0/vortex/templates/agent/CLAUDE.md +44 -0
- vortex_cli-4.21.0/vortex/templates/agent/skills/puakma-database/SKILL.md +107 -0
- vortex_cli-4.21.0/vortex/templates/agent/skills/puakma-design-elements/SKILL.md +95 -0
- vortex_cli-4.21.0/vortex/templates/agent/skills/puakma-overview/SKILL.md +64 -0
- vortex_cli-4.21.0/vortex/templates/agent/skills/vortex-workflow/SKILL.md +103 -0
- vortex_cli-4.21.0/vortex/templates/agent/vortex.code-snippets +437 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/util.py +13 -13
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/workspace.py +35 -1
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex_cli.egg-info/PKG-INFO +48 -3
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex_cli.egg-info/SOURCES.txt +12 -0
- vortex_cli-4.20.0/vortex/commands/docs.py +0 -44
- vortex_cli-4.20.0/vortex/commands/use.py +0 -28
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/LICENSE +0 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/setup.cfg +0 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/__init__.py +0 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/__main__.py +0 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/colour.py +0 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/commands/__init__.py +0 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/commands/clean.py +0 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/commands/code.py +0 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/commands/config.py +0 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/commands/copy.py +0 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/commands/delete.py +0 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/commands/execute.py +0 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/commands/find.py +0 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/constants.py +0 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/docs/Blackbook.pdf +0 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/lib/puakma-6.0.40.jar +0 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/logging.py +0 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex/spinner.py +0 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex_cli.egg-info/dependency_links.txt +0 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex_cli.egg-info/entry_points.txt +0 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.0}/vortex_cli.egg-info/requires.txt +0 -0
- {vortex_cli-4.20.0 → vortex_cli-4.21.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: 4.
|
|
3
|
+
Version: 4.21.0
|
|
4
4
|
Summary: Vortex CLI
|
|
5
5
|
Author-email: Jordan Amos <jordan.amos@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -121,7 +121,10 @@ For a full list of commands see `--help`.
|
|
|
121
121
|
- `new`: Create new Design Objects, Applications, or Keywords. Use `--update <ID>` to update instead. Run without flags to launch an interactive wizard.
|
|
122
122
|
- `copy`: Copy a Design Object from one application to another.
|
|
123
123
|
- `delete`: Delete Design Objects by ID.
|
|
124
|
-
- `db`: Interact with Database Connections.
|
|
124
|
+
- `db`: Interact with Database Connections. Accepts `--server`/`-s` like other commands for one-off queries against another server.
|
|
125
|
+
- `schema`: Manage the Puakma data dictionary (PMATABLE/ATTRIBUTE): record table/column definitions and print the DDL to run by hand. Never executes DDL.
|
|
126
|
+
- `compile` (or `build`): Compile an application's Java Design Objects into `zbin/` using `javac`.
|
|
127
|
+
- `agent`: Generate agent/editor support files (CLAUDE.md, Puakma skills, code snippets) in the workspace.
|
|
125
128
|
- `docs`: Open the Tornado Server Blackbook.
|
|
126
129
|
- `execute`: Execute a command on the server.
|
|
127
130
|
|
|
@@ -144,7 +147,7 @@ vortex new object
|
|
|
144
147
|
# → pick app from locally cloned list
|
|
145
148
|
# → pick design type
|
|
146
149
|
# → enter name
|
|
147
|
-
# → enter content type (resource only)
|
|
150
|
+
# → enter content type (resource/documentation only)
|
|
148
151
|
# → enter parent page, open action, save action (page only)
|
|
149
152
|
# → preview and confirm
|
|
150
153
|
|
|
@@ -163,3 +166,45 @@ Pass `--yes` (or `-y`) to skip Y/y confirmation prompts, useful for scripting:
|
|
|
163
166
|
vortex --yes delete 123 456
|
|
164
167
|
vortex -y new object --name MyAction --app-id 10 --type action
|
|
165
168
|
```
|
|
169
|
+
|
|
170
|
+
### Compiling Java Design Objects
|
|
171
|
+
|
|
172
|
+
`vortex compile` (alias `build`) compiles an application's Java Design Objects into `zbin/`
|
|
173
|
+
with a real `javac`, independent of the IDE's incremental build - real compile errors, a
|
|
174
|
+
nonzero exit code on failure, and no stub class files. `javac` is found via `java_home` in the
|
|
175
|
+
server config, then `$JAVA_HOME`, then `PATH` (`--javac PATH` to override), and `--release` is
|
|
176
|
+
derived from the application's Java class version. It also warns when nested/inner/anonymous
|
|
177
|
+
classes are produced, since these cannot be deployed as Design Objects.
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
vortex compile # compile all locally cloned apps for the server
|
|
181
|
+
vortex compile 13 # compile one app
|
|
182
|
+
vortex compile 13 --upload # also upload changed .class files
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
While `vortex watch` is running, `--upload` isn't needed - watch uploads new class files from
|
|
186
|
+
`zbin/` automatically.
|
|
187
|
+
|
|
188
|
+
### Managing the Data Dictionary
|
|
189
|
+
|
|
190
|
+
`vortex schema` records design-time table and column definitions in the Puakma data
|
|
191
|
+
dictionary (the PMATABLE/ATTRIBUTE tables, which also back `vortex db --schema`) and prints
|
|
192
|
+
the matching DDL for you to run by hand. It never executes DDL, and deletes only remove
|
|
193
|
+
dictionary rows - never real tables or columns.
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
vortex schema mydb --add-table invoice --description "Customer invoices"
|
|
197
|
+
vortex schema mydb --add-column invoice invoice_id --type BIGINT --pk --auto-increment
|
|
198
|
+
vortex schema mydb --add-column invoice total --type NUMERIC --size 10,2 --not-null
|
|
199
|
+
vortex schema mydb --add-column invoice customer_id --type BIGINT --ref customer.customer_id
|
|
200
|
+
vortex schema mydb --ddl invoice # print CREATE TABLE from the dictionary
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Agent Support Files
|
|
204
|
+
|
|
205
|
+
`vortex agent` copies bundled support files into the workspace `.vscode` directory so they are
|
|
206
|
+
included in the generated code-workspace: a `CLAUDE.md` with Puakma ground rules for coding
|
|
207
|
+
agents, a `.claude/skills/` library covering Puakma development and the vortex workflow, and a
|
|
208
|
+
`vortex.code-snippets` file with common Puakma Java/HTML snippets. Existing files are never
|
|
209
|
+
overwritten, so they are safe to customise. These files are also generated automatically on
|
|
210
|
+
`vortex --init` and before `vortex code` opens the workspace.
|
|
@@ -79,7 +79,10 @@ For a full list of commands see `--help`.
|
|
|
79
79
|
- `new`: Create new Design Objects, Applications, or Keywords. Use `--update <ID>` to update instead. Run without flags to launch an interactive wizard.
|
|
80
80
|
- `copy`: Copy a Design Object from one application to another.
|
|
81
81
|
- `delete`: Delete Design Objects by ID.
|
|
82
|
-
- `db`: Interact with Database Connections.
|
|
82
|
+
- `db`: Interact with Database Connections. Accepts `--server`/`-s` like other commands for one-off queries against another server.
|
|
83
|
+
- `schema`: Manage the Puakma data dictionary (PMATABLE/ATTRIBUTE): record table/column definitions and print the DDL to run by hand. Never executes DDL.
|
|
84
|
+
- `compile` (or `build`): Compile an application's Java Design Objects into `zbin/` using `javac`.
|
|
85
|
+
- `agent`: Generate agent/editor support files (CLAUDE.md, Puakma skills, code snippets) in the workspace.
|
|
83
86
|
- `docs`: Open the Tornado Server Blackbook.
|
|
84
87
|
- `execute`: Execute a command on the server.
|
|
85
88
|
|
|
@@ -102,7 +105,7 @@ vortex new object
|
|
|
102
105
|
# → pick app from locally cloned list
|
|
103
106
|
# → pick design type
|
|
104
107
|
# → enter name
|
|
105
|
-
# → enter content type (resource only)
|
|
108
|
+
# → enter content type (resource/documentation only)
|
|
106
109
|
# → enter parent page, open action, save action (page only)
|
|
107
110
|
# → preview and confirm
|
|
108
111
|
|
|
@@ -121,3 +124,45 @@ Pass `--yes` (or `-y`) to skip Y/y confirmation prompts, useful for scripting:
|
|
|
121
124
|
vortex --yes delete 123 456
|
|
122
125
|
vortex -y new object --name MyAction --app-id 10 --type action
|
|
123
126
|
```
|
|
127
|
+
|
|
128
|
+
### Compiling Java Design Objects
|
|
129
|
+
|
|
130
|
+
`vortex compile` (alias `build`) compiles an application's Java Design Objects into `zbin/`
|
|
131
|
+
with a real `javac`, independent of the IDE's incremental build - real compile errors, a
|
|
132
|
+
nonzero exit code on failure, and no stub class files. `javac` is found via `java_home` in the
|
|
133
|
+
server config, then `$JAVA_HOME`, then `PATH` (`--javac PATH` to override), and `--release` is
|
|
134
|
+
derived from the application's Java class version. It also warns when nested/inner/anonymous
|
|
135
|
+
classes are produced, since these cannot be deployed as Design Objects.
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
vortex compile # compile all locally cloned apps for the server
|
|
139
|
+
vortex compile 13 # compile one app
|
|
140
|
+
vortex compile 13 --upload # also upload changed .class files
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
While `vortex watch` is running, `--upload` isn't needed - watch uploads new class files from
|
|
144
|
+
`zbin/` automatically.
|
|
145
|
+
|
|
146
|
+
### Managing the Data Dictionary
|
|
147
|
+
|
|
148
|
+
`vortex schema` records design-time table and column definitions in the Puakma data
|
|
149
|
+
dictionary (the PMATABLE/ATTRIBUTE tables, which also back `vortex db --schema`) and prints
|
|
150
|
+
the matching DDL for you to run by hand. It never executes DDL, and deletes only remove
|
|
151
|
+
dictionary rows - never real tables or columns.
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
vortex schema mydb --add-table invoice --description "Customer invoices"
|
|
155
|
+
vortex schema mydb --add-column invoice invoice_id --type BIGINT --pk --auto-increment
|
|
156
|
+
vortex schema mydb --add-column invoice total --type NUMERIC --size 10,2 --not-null
|
|
157
|
+
vortex schema mydb --add-column invoice customer_id --type BIGINT --ref customer.customer_id
|
|
158
|
+
vortex schema mydb --ddl invoice # print CREATE TABLE from the dictionary
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Agent Support Files
|
|
162
|
+
|
|
163
|
+
`vortex agent` copies bundled support files into the workspace `.vscode` directory so they are
|
|
164
|
+
included in the generated code-workspace: a `CLAUDE.md` with Puakma ground rules for coding
|
|
165
|
+
agents, a `.claude/skills/` library covering Puakma development and the vortex workflow, and a
|
|
166
|
+
`vortex.code-snippets` file with common Puakma Java/HTML snippets. Existing files are never
|
|
167
|
+
overwritten, so they are safe to customise. These files are also generated automatically on
|
|
168
|
+
`vortex --init` and before `vortex code` opens the workspace.
|
|
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|
|
5
5
|
|
|
6
6
|
[project]
|
|
7
7
|
name = "vortex_cli"
|
|
8
|
-
version = "4.
|
|
8
|
+
version = "4.21.0"
|
|
9
9
|
description = "Vortex CLI"
|
|
10
10
|
requires-python = ">=3.10"
|
|
11
11
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
@@ -32,7 +32,15 @@ classifiers = [
|
|
|
32
32
|
vortex = "vortex.__main__:main"
|
|
33
33
|
|
|
34
34
|
[tool.setuptools.package-data]
|
|
35
|
-
vortex = [
|
|
35
|
+
vortex = [
|
|
36
|
+
"lib/puakma-6.0.40.jar",
|
|
37
|
+
"docs/Blackbook.pdf",
|
|
38
|
+
"docs/Blackbook v2.md",
|
|
39
|
+
"docs/index.html",
|
|
40
|
+
"docs/marked.min.js",
|
|
41
|
+
"templates/agent/*",
|
|
42
|
+
"templates/agent/skills/*/*",
|
|
43
|
+
]
|
|
36
44
|
|
|
37
45
|
[tool.mypy]
|
|
38
46
|
check_untyped_defs = true
|
|
@@ -32,7 +32,7 @@ _EXECUTE_PARSER_DESCRIPTION = """\
|
|
|
32
32
|
"""
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
def _check_int_in_range(val: str, min_: int =
|
|
35
|
+
def _check_int_in_range(val: str, min_: int = 1, max_: int | None = 50) -> int:
|
|
36
36
|
new_val = int(val)
|
|
37
37
|
if new_val < min_ or (max_ is not None and new_val > max_):
|
|
38
38
|
raise ArgumentTypeError(f"%s is not between {min_} and {max_}. " % val)
|
|
@@ -90,8 +90,9 @@ def validate_args(
|
|
|
90
90
|
) -> None:
|
|
91
91
|
if args.command == "new":
|
|
92
92
|
msg = None
|
|
93
|
-
is_update_mode =
|
|
94
|
-
if args.subcommand
|
|
93
|
+
is_update_mode = getattr(args, "update_ids", None) is not None
|
|
94
|
+
if args.subcommand in ("object", "obj"):
|
|
95
|
+
update_missing_ids = is_update_mode and not args.update_ids
|
|
95
96
|
missing_required_fields = not is_update_mode and not (
|
|
96
97
|
args.name and args.app_id and args.design_type
|
|
97
98
|
)
|
|
@@ -114,7 +115,9 @@ def validate_args(
|
|
|
114
115
|
or args.parent_page
|
|
115
116
|
or args.content_type
|
|
116
117
|
)
|
|
117
|
-
if
|
|
118
|
+
if update_missing_ids:
|
|
119
|
+
msg = "--update requires at least one Design Object ID"
|
|
120
|
+
elif missing_required_fields and not wizard_mode:
|
|
118
121
|
msg = "--name, --app-id, and --type are required unless using --update"
|
|
119
122
|
elif missing_content_type:
|
|
120
123
|
msg = (
|
|
@@ -670,11 +673,129 @@ def add_db_parser(command_parser: _SubParsersAction[ArgumentParser]) -> Argument
|
|
|
670
673
|
action="store_true",
|
|
671
674
|
help="Set this flag when running queries for altering/updating the database",
|
|
672
675
|
)
|
|
676
|
+
_add_server_option(db_parser)
|
|
673
677
|
return db_parser
|
|
674
678
|
|
|
675
679
|
|
|
676
680
|
def add_docs_parser(command_parser: _SubParsersAction[ArgumentParser]) -> None:
|
|
677
|
-
command_parser.add_parser(
|
|
681
|
+
docs_parser = command_parser.add_parser(
|
|
682
|
+
"docs", help="Open the Tornado Server Blackbook"
|
|
683
|
+
)
|
|
684
|
+
docs_parser.add_argument(
|
|
685
|
+
"--serve",
|
|
686
|
+
action="store_true",
|
|
687
|
+
help=(
|
|
688
|
+
"Serve the Blackbook v2 as a searchable local website "
|
|
689
|
+
"instead of opening the PDF"
|
|
690
|
+
),
|
|
691
|
+
)
|
|
692
|
+
docs_parser.add_argument(
|
|
693
|
+
"--port",
|
|
694
|
+
type=int,
|
|
695
|
+
default=8477,
|
|
696
|
+
metavar="PORT",
|
|
697
|
+
help="The port to serve the docs on with --serve [%(default)s]",
|
|
698
|
+
)
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
def add_schema_parser(command_parser: _SubParsersAction[ArgumentParser]) -> None:
|
|
702
|
+
schema_parser = command_parser.add_parser(
|
|
703
|
+
"schema",
|
|
704
|
+
help=(
|
|
705
|
+
"Manage the Puakma data dictionary (PMATABLE/ATTRIBUTE): record "
|
|
706
|
+
"design-time table/column definitions and print the DDL to run "
|
|
707
|
+
"by hand. Never executes DDL"
|
|
708
|
+
),
|
|
709
|
+
)
|
|
710
|
+
schema_parser.add_argument(
|
|
711
|
+
"database", help="The database name whose dictionary to manage"
|
|
712
|
+
)
|
|
713
|
+
schema_mutex = schema_parser.add_mutually_exclusive_group(required=True)
|
|
714
|
+
schema_mutex.add_argument("--add-table", metavar="TABLE")
|
|
715
|
+
schema_mutex.add_argument(
|
|
716
|
+
"--delete-table",
|
|
717
|
+
metavar="TABLE",
|
|
718
|
+
help="Remove a table definition (does NOT drop the real table)",
|
|
719
|
+
)
|
|
720
|
+
schema_mutex.add_argument("--add-column", nargs=2, metavar=("TABLE", "COLUMN"))
|
|
721
|
+
schema_mutex.add_argument("--update-column", nargs=2, metavar=("TABLE", "COLUMN"))
|
|
722
|
+
schema_mutex.add_argument(
|
|
723
|
+
"--delete-column",
|
|
724
|
+
nargs=2,
|
|
725
|
+
metavar=("TABLE", "COLUMN"),
|
|
726
|
+
help="Remove a column definition (does NOT alter the real table)",
|
|
727
|
+
)
|
|
728
|
+
schema_mutex.add_argument(
|
|
729
|
+
"--ddl",
|
|
730
|
+
metavar="TABLE",
|
|
731
|
+
dest="ddl_table",
|
|
732
|
+
help="Print CREATE TABLE DDL generated from the dictionary definitions",
|
|
733
|
+
)
|
|
734
|
+
col_group = schema_parser.add_argument_group("Column Options")
|
|
735
|
+
col_group.add_argument("--type", dest="type_", metavar="SQL_TYPE")
|
|
736
|
+
col_group.add_argument("--size", metavar="N", help="e.g. 50 or '10,2'")
|
|
737
|
+
col_group.add_argument("--not-null", action="store_true")
|
|
738
|
+
col_group.add_argument("--pk", action="store_true")
|
|
739
|
+
col_group.add_argument("--auto-increment", action="store_true")
|
|
740
|
+
col_group.add_argument("--unique", action="store_true")
|
|
741
|
+
col_group.add_argument("--default", metavar="VALUE")
|
|
742
|
+
col_group.add_argument(
|
|
743
|
+
"--ref", metavar="TABLE.COLUMN", help="Foreign key reference"
|
|
744
|
+
)
|
|
745
|
+
col_group.add_argument("--description", metavar="TEXT")
|
|
746
|
+
col_group.add_argument("--position", type=int, metavar="N")
|
|
747
|
+
_add_server_option(schema_parser)
|
|
748
|
+
|
|
749
|
+
|
|
750
|
+
def add_compile_parser(command_parser: _SubParsersAction[ArgumentParser]) -> None:
|
|
751
|
+
compile_parser = command_parser.add_parser(
|
|
752
|
+
"compile",
|
|
753
|
+
aliases=["build"],
|
|
754
|
+
help=(
|
|
755
|
+
"Compile an app's Java design elements into zbin/ with javac. "
|
|
756
|
+
"Real compile errors, no IDE error-stub classes"
|
|
757
|
+
),
|
|
758
|
+
)
|
|
759
|
+
compile_parser.add_argument(
|
|
760
|
+
"app_ids",
|
|
761
|
+
nargs="*",
|
|
762
|
+
type=int,
|
|
763
|
+
metavar="APP_ID",
|
|
764
|
+
help=(
|
|
765
|
+
"App id(s) to compile. Default is all locally cloned apps " "for the server"
|
|
766
|
+
),
|
|
767
|
+
)
|
|
768
|
+
compile_parser.add_argument(
|
|
769
|
+
"--upload",
|
|
770
|
+
"-u",
|
|
771
|
+
action="store_true",
|
|
772
|
+
help=(
|
|
773
|
+
"Upload changed .class files after a successful compile. "
|
|
774
|
+
"Not needed while 'vortex watch' is running - watch uploads "
|
|
775
|
+
"new class files automatically"
|
|
776
|
+
),
|
|
777
|
+
)
|
|
778
|
+
compile_parser.add_argument(
|
|
779
|
+
"--javac",
|
|
780
|
+
metavar="PATH",
|
|
781
|
+
dest="javac_path",
|
|
782
|
+
help=(
|
|
783
|
+
"Path to javac. Default: <java_home>/bin/javac from the server "
|
|
784
|
+
"config, else $JAVA_HOME, else javac on PATH"
|
|
785
|
+
),
|
|
786
|
+
)
|
|
787
|
+
_add_server_option(compile_parser)
|
|
788
|
+
|
|
789
|
+
|
|
790
|
+
def add_agent_parser(command_parser: _SubParsersAction[ArgumentParser]) -> None:
|
|
791
|
+
command_parser.add_parser(
|
|
792
|
+
"agent",
|
|
793
|
+
help=(
|
|
794
|
+
"Generate agent/editor support files in the workspace "
|
|
795
|
+
"(CLAUDE.md, Puakma skills, code snippets). Existing files "
|
|
796
|
+
"are never overwritten"
|
|
797
|
+
),
|
|
798
|
+
)
|
|
678
799
|
|
|
679
800
|
|
|
680
801
|
def add_execute_parser(command_parser: _SubParsersAction[ArgumentParser]) -> None:
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
from vortex.workspace import Workspace
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger("vortex")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def agent(workspace: Workspace) -> int:
|
|
11
|
+
"""
|
|
12
|
+
Generates the agent/editor support files (CLAUDE.md, Puakma skills,
|
|
13
|
+
code snippets) in the workspace .vscode directory. Existing files are
|
|
14
|
+
never overwritten.
|
|
15
|
+
"""
|
|
16
|
+
written, skipped = workspace.init_agent_files()
|
|
17
|
+
for path in written:
|
|
18
|
+
logger.info(f"Created '{path}'")
|
|
19
|
+
for path in skipped:
|
|
20
|
+
logger.debug(f"Skipped existing '{path}'")
|
|
21
|
+
if not written:
|
|
22
|
+
logger.info(f"All {len(skipped)} agent file(s) already exist. Nothing to do.")
|
|
23
|
+
else:
|
|
24
|
+
logger.info(
|
|
25
|
+
f"Created {len(written)} file(s), kept {len(skipped)} existing file(s)."
|
|
26
|
+
)
|
|
27
|
+
return 0
|
|
@@ -77,7 +77,7 @@ def _parse_app_xml(
|
|
|
77
77
|
server: PuakmaServer, app_xml: ET.Element, app_id: int
|
|
78
78
|
) -> tuple[PuakmaApplication, ET.Element]:
|
|
79
79
|
app_ele = app_xml.find("puakmaApplication", namespaces=None)
|
|
80
|
-
if
|
|
80
|
+
if app_ele is None:
|
|
81
81
|
raise ValueError(f"Application [{app_id}] does not exist")
|
|
82
82
|
|
|
83
83
|
db_connections: list[DatabaseConnection] = []
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import configparser
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
import shutil
|
|
8
|
+
import subprocess
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from vortex.models import DesignObject
|
|
13
|
+
from vortex.models import DesignObjectAmbiguousError
|
|
14
|
+
from vortex.models import DesignObjectNotFound
|
|
15
|
+
from vortex.models import DesignType
|
|
16
|
+
from vortex.models import PuakmaApplication
|
|
17
|
+
from vortex.models import PuakmaServer
|
|
18
|
+
from vortex.workspace import Workspace
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger("vortex")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _find_javac(
|
|
24
|
+
workspace: Workspace, server: PuakmaServer, override: str | None
|
|
25
|
+
) -> str | None:
|
|
26
|
+
if override:
|
|
27
|
+
if Path(override).is_file():
|
|
28
|
+
return override
|
|
29
|
+
return shutil.which(override)
|
|
30
|
+
config = configparser.ConfigParser()
|
|
31
|
+
config.read(workspace.server_config_file)
|
|
32
|
+
java_home = config.get(server.name, "java_home", fallback=None) or os.getenv(
|
|
33
|
+
"JAVA_HOME"
|
|
34
|
+
)
|
|
35
|
+
if java_home:
|
|
36
|
+
exe = "javac.exe" if sys.platform == "win32" else "javac"
|
|
37
|
+
candidate = Path(java_home) / "bin" / exe
|
|
38
|
+
if candidate.is_file():
|
|
39
|
+
return str(candidate)
|
|
40
|
+
return shutil.which("javac")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _build_classpath(workspace: Workspace, server: PuakmaServer, app_dir: Path) -> str:
|
|
44
|
+
import vortex
|
|
45
|
+
|
|
46
|
+
lib_dir = Path(vortex.__file__).parent / "lib"
|
|
47
|
+
entries = [str(jar) for jar in sorted(lib_dir.glob("*.jar"))]
|
|
48
|
+
entries.extend(str(jar) for jar in sorted((app_dir / "zlib").rglob("*.jar")))
|
|
49
|
+
config = configparser.ConfigParser()
|
|
50
|
+
config.read(workspace.server_config_file)
|
|
51
|
+
server_libs = config.get(server.name, "lib_path", fallback=None)
|
|
52
|
+
if server_libs:
|
|
53
|
+
entries.extend(p for p in server_libs.split(",") if p)
|
|
54
|
+
entries.append(str(app_dir / "zbin"))
|
|
55
|
+
return os.pathsep.join(entries)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _compile_app(
|
|
59
|
+
workspace: Workspace,
|
|
60
|
+
server: PuakmaServer,
|
|
61
|
+
app: PuakmaApplication,
|
|
62
|
+
javac: str,
|
|
63
|
+
) -> tuple[int, list[Path]]:
|
|
64
|
+
"""
|
|
65
|
+
Compiles an app's Java design sources into zbin/ with javac.
|
|
66
|
+
Returns (exit_code, compiled_class_files).
|
|
67
|
+
"""
|
|
68
|
+
app_dir = workspace.path / app.path
|
|
69
|
+
sources = [
|
|
70
|
+
str(f)
|
|
71
|
+
for dir_name in DesignType.source_dirs()
|
|
72
|
+
for f in sorted((app_dir / dir_name).rglob("*.java"))
|
|
73
|
+
]
|
|
74
|
+
if not sources:
|
|
75
|
+
logger.info(f"{app}: no Java sources to compile")
|
|
76
|
+
return 0, []
|
|
77
|
+
|
|
78
|
+
zbin = app_dir / "zbin"
|
|
79
|
+
zbin.mkdir(exist_ok=True)
|
|
80
|
+
|
|
81
|
+
cmd = [javac, "-d", str(zbin), "-cp", _build_classpath(workspace, server, app_dir)]
|
|
82
|
+
if app.java_class_version:
|
|
83
|
+
# Class file major version N == Java release N - 44 (61 -> 17)
|
|
84
|
+
cmd.extend(["--release", str(app.java_class_version[0] - 44)])
|
|
85
|
+
cmd.extend(sources)
|
|
86
|
+
|
|
87
|
+
logger.info(f"Compiling {len(sources)} source file(s) for {app}...")
|
|
88
|
+
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
89
|
+
if result.stdout.strip():
|
|
90
|
+
print(result.stdout, end="")
|
|
91
|
+
if result.returncode != 0:
|
|
92
|
+
print(result.stderr, file=sys.stderr, end="")
|
|
93
|
+
logger.error(f"{app}: compilation FAILED")
|
|
94
|
+
return 1, []
|
|
95
|
+
if result.stderr.strip():
|
|
96
|
+
# javac warnings
|
|
97
|
+
print(result.stderr, file=sys.stderr, end="")
|
|
98
|
+
|
|
99
|
+
compiled = sorted(zbin.rglob("*.class"))
|
|
100
|
+
nested = [c for c in compiled if "$" in c.name]
|
|
101
|
+
if nested:
|
|
102
|
+
names = ", ".join(sorted({c.name for c in nested}))
|
|
103
|
+
logger.warning(
|
|
104
|
+
f"{app}: nested/inner/anonymous classes compiled ({names}) - "
|
|
105
|
+
"these CANNOT be deployed as design elements; refactor to "
|
|
106
|
+
"top-level SHARED_CODE classes"
|
|
107
|
+
)
|
|
108
|
+
logger.info(f"{app}: compilation OK")
|
|
109
|
+
return 0, compiled
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
async def _aupload_classes(
|
|
113
|
+
workspace: Workspace,
|
|
114
|
+
server: PuakmaServer,
|
|
115
|
+
to_upload: list[tuple[PuakmaApplication, DesignObject]],
|
|
116
|
+
) -> int:
|
|
117
|
+
ret = 0
|
|
118
|
+
async with server as s:
|
|
119
|
+
await s.server_designer.ainitiate_connection()
|
|
120
|
+
for _app, obj in to_upload:
|
|
121
|
+
ok = await obj.aupload(s.download_designer, upload_source=False)
|
|
122
|
+
ret |= 0 if ok else 1
|
|
123
|
+
for app in {app for app, _obj in to_upload}:
|
|
124
|
+
workspace.mkdir(app)
|
|
125
|
+
return ret
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _collect_uploads(
|
|
129
|
+
app: PuakmaApplication, class_files: list[Path]
|
|
130
|
+
) -> list[tuple[PuakmaApplication, DesignObject]]:
|
|
131
|
+
"""
|
|
132
|
+
Matches compiled top-level classes to design objects, skipping classes
|
|
133
|
+
whose bytes already match the manifest's design data.
|
|
134
|
+
"""
|
|
135
|
+
to_upload = []
|
|
136
|
+
for class_file in class_files:
|
|
137
|
+
if "$" in class_file.name:
|
|
138
|
+
continue
|
|
139
|
+
try:
|
|
140
|
+
_, obj = app.lookup_design_obj(class_file.stem)
|
|
141
|
+
except DesignObjectNotFound:
|
|
142
|
+
continue
|
|
143
|
+
except DesignObjectAmbiguousError as e:
|
|
144
|
+
logger.warning(f"Not uploading '{class_file.name}': {e}")
|
|
145
|
+
continue
|
|
146
|
+
if not obj.design_type.is_java_type or obj.is_jar_library:
|
|
147
|
+
continue
|
|
148
|
+
class_bytes = class_file.read_bytes()
|
|
149
|
+
try:
|
|
150
|
+
unchanged = obj.design_data == class_bytes
|
|
151
|
+
except Exception:
|
|
152
|
+
unchanged = False
|
|
153
|
+
if unchanged:
|
|
154
|
+
logger.debug(f"{obj.name}: unchanged, skipping upload")
|
|
155
|
+
continue
|
|
156
|
+
obj.design_data = class_bytes
|
|
157
|
+
to_upload.append((app, obj))
|
|
158
|
+
return to_upload
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def compile_(
|
|
162
|
+
workspace: Workspace,
|
|
163
|
+
server: PuakmaServer,
|
|
164
|
+
app_ids: list[int],
|
|
165
|
+
*,
|
|
166
|
+
upload: bool = False,
|
|
167
|
+
javac_path: str | None = None,
|
|
168
|
+
) -> int:
|
|
169
|
+
javac = _find_javac(workspace, server, javac_path)
|
|
170
|
+
if not javac:
|
|
171
|
+
logger.error(
|
|
172
|
+
"javac not found. Set 'java_home' in the server config, "
|
|
173
|
+
"$JAVA_HOME, or pass --javac PATH"
|
|
174
|
+
)
|
|
175
|
+
return 1
|
|
176
|
+
logger.debug(f"Using javac: {javac}")
|
|
177
|
+
|
|
178
|
+
apps = workspace.listapps(server)
|
|
179
|
+
if app_ids:
|
|
180
|
+
apps = [app for app in apps if app.id in app_ids]
|
|
181
|
+
missing = set(app_ids) - {app.id for app in apps}
|
|
182
|
+
if missing:
|
|
183
|
+
logger.error(f"No locally cloned app(s) with id(s): {sorted(missing)}")
|
|
184
|
+
return 1
|
|
185
|
+
if not apps:
|
|
186
|
+
logger.error("No locally cloned applications to compile")
|
|
187
|
+
return 1
|
|
188
|
+
|
|
189
|
+
ret = 0
|
|
190
|
+
compiled_per_app: list[tuple[PuakmaApplication, list[Path]]] = []
|
|
191
|
+
for app in apps:
|
|
192
|
+
app_ret, class_files = _compile_app(workspace, server, app, javac)
|
|
193
|
+
ret |= app_ret
|
|
194
|
+
if app_ret == 0 and class_files:
|
|
195
|
+
compiled_per_app.append((app, class_files))
|
|
196
|
+
|
|
197
|
+
if upload and ret == 0:
|
|
198
|
+
to_upload: list[tuple[PuakmaApplication, DesignObject]] = []
|
|
199
|
+
for app, class_files in compiled_per_app:
|
|
200
|
+
to_upload.extend(_collect_uploads(app, class_files))
|
|
201
|
+
if to_upload:
|
|
202
|
+
logger.info(f"Uploading {len(to_upload)} compiled class(es)...")
|
|
203
|
+
ret |= asyncio.run(_aupload_classes(workspace, server, to_upload))
|
|
204
|
+
else:
|
|
205
|
+
logger.info("All compiled classes match the server. Nothing to upload.")
|
|
206
|
+
elif upload and ret != 0:
|
|
207
|
+
logger.warning("Skipping upload because compilation failed")
|
|
208
|
+
return ret
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
import os
|
|
5
|
+
import re
|
|
5
6
|
import subprocess
|
|
6
7
|
import sys
|
|
7
8
|
import tempfile
|
|
@@ -159,14 +160,18 @@ def db(
|
|
|
159
160
|
logger.error(f"No cloned applications with DB Connection ID '{conn_id}'")
|
|
160
161
|
return 1
|
|
161
162
|
if sql and not update:
|
|
162
|
-
|
|
163
|
+
# Strip a trailing ';' and don't stack LIMITs so the appended
|
|
164
|
+
# clause can't create invalid SQL
|
|
165
|
+
sql = sql.strip().rstrip(";").rstrip()
|
|
166
|
+
if not re.search(r"\bLIMIT\b", sql, re.IGNORECASE):
|
|
167
|
+
sql = sql + f" LIMIT {limit_n_results}"
|
|
163
168
|
else:
|
|
164
|
-
db_name = str(database).lower()
|
|
169
|
+
db_name = str(database).lower().replace("'", "''")
|
|
165
170
|
update = False
|
|
166
171
|
truncate_cols = False
|
|
167
172
|
conn_id = server.puakma_db_conn_id
|
|
168
173
|
if schema_table is not None:
|
|
169
|
-
sql = GET_SCHEMA_SQL % (db_name, schema_table)
|
|
174
|
+
sql = GET_SCHEMA_SQL % (db_name, schema_table.lower().replace("'", "''"))
|
|
170
175
|
elif list_tables:
|
|
171
176
|
sql = LIST_TABLES_SQL % db_name
|
|
172
177
|
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import functools
|
|
4
|
+
import http.server
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
import platform
|
|
8
|
+
import shutil
|
|
9
|
+
import subprocess
|
|
10
|
+
import webbrowser
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger("vortex")
|
|
15
|
+
|
|
16
|
+
DOCS_DIR = Path(os.path.dirname(__file__), "..", "docs").resolve()
|
|
17
|
+
DEFAULT_PORT = 8477
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class _DocsRequestHandler(http.server.SimpleHTTPRequestHandler):
|
|
21
|
+
def log_message(self, fmt: str, *args: Any) -> None:
|
|
22
|
+
logger.debug(f"{self.address_string()} - {fmt % args}")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _serve(port: int) -> int:
|
|
26
|
+
handler = functools.partial(_DocsRequestHandler, directory=str(DOCS_DIR))
|
|
27
|
+
try:
|
|
28
|
+
server = http.server.ThreadingHTTPServer(("127.0.0.1", port), handler)
|
|
29
|
+
except OSError as e:
|
|
30
|
+
logger.error(f"Unable to start the docs server on port {port}: {e}")
|
|
31
|
+
return 1
|
|
32
|
+
|
|
33
|
+
url = f"http://127.0.0.1:{port}/"
|
|
34
|
+
logger.info(f"Serving the Blackbook at {url} (Ctrl+C to stop)")
|
|
35
|
+
webbrowser.open(url)
|
|
36
|
+
try:
|
|
37
|
+
server.serve_forever()
|
|
38
|
+
except KeyboardInterrupt:
|
|
39
|
+
pass
|
|
40
|
+
finally:
|
|
41
|
+
server.server_close()
|
|
42
|
+
return 0
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _open_pdf() -> int:
|
|
46
|
+
system = platform.system()
|
|
47
|
+
doc_path = DOCS_DIR / "Blackbook.pdf"
|
|
48
|
+
|
|
49
|
+
cmd = None
|
|
50
|
+
shell = False
|
|
51
|
+
|
|
52
|
+
if system == "Windows":
|
|
53
|
+
cmd = "start"
|
|
54
|
+
shell = True
|
|
55
|
+
# MacOS
|
|
56
|
+
elif system == "Darwin":
|
|
57
|
+
cmd = shutil.which("open")
|
|
58
|
+
elif system == "Linux":
|
|
59
|
+
cmd = shutil.which("xdg-open")
|
|
60
|
+
|
|
61
|
+
if cmd is None:
|
|
62
|
+
logger.error("Unable to determine default program to open file.")
|
|
63
|
+
return 1
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
subprocess.Popen(
|
|
67
|
+
[cmd, str(doc_path)],
|
|
68
|
+
shell=shell,
|
|
69
|
+
stdout=subprocess.DEVNULL,
|
|
70
|
+
stderr=subprocess.DEVNULL,
|
|
71
|
+
)
|
|
72
|
+
except FileNotFoundError:
|
|
73
|
+
logger.error(f"Command '{cmd}' not found")
|
|
74
|
+
return 1
|
|
75
|
+
|
|
76
|
+
return 0
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def docs(serve: bool = False, port: int = DEFAULT_PORT) -> int:
|
|
80
|
+
if serve:
|
|
81
|
+
return _serve(port)
|
|
82
|
+
return _open_pdf()
|