apexrest 1.3.0-beta.1 → 1.3.0

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 (182) hide show
  1. package/README.md +90 -140
  2. package/dist/codex-compat/.claude-plugin/marketplace.json +20 -0
  3. package/dist/codex-compat/plugins/apexrest-apex/.claude-plugin/plugin.json +28 -0
  4. package/dist/codex-compat/plugins/apexrest-apex/.codex-plugin/plugin.json +7 -7
  5. package/dist/codex-compat/plugins/apexrest-apex/resources/blocks/generator.json +1 -1
  6. package/dist/codex-compat/plugins/apexrest-apex/resources/blocks/manifest.json +6 -6
  7. package/dist/codex-compat/plugins/apexrest-apex/resources/blocks/packages/analytics/status-summary/1.0.0/block.yaml +1 -1
  8. package/dist/codex-compat/plugins/apexrest-apex/resources/blocks/packages/crud/report-dialog/1.0.0/block.yaml +1 -1
  9. package/dist/codex-compat/plugins/apexrest-apex/resources/blocks/packages/read/detail/1.0.0/block.yaml +1 -1
  10. package/dist/codex-compat/plugins/apexrest-apex/resources/blocks/packages/read/filtered-list/1.0.0/block.yaml +1 -1
  11. package/dist/codex-compat/plugins/apexrest-apex/resources/blocks/packages/read/history/1.0.0/block.yaml +1 -1
  12. package/dist/codex-compat/plugins/apexrest-apex/resources/blocks/packages/read/master-detail/1.0.0/block.yaml +1 -1
  13. package/dist/codex-compat/plugins/apexrest-apex/resources/schemas/composer-blueprint.schema.json +5 -4
  14. package/dist/codex-compat/plugins/apexrest-apex/resources/schemas/composer-contract.schema.json +2 -2
  15. package/dist/codex-compat/plugins/apexrest-apex/resources/schemas/composer-entity.schema.json +2 -1
  16. package/dist/codex-compat/plugins/apexrest-apex/resources/schemas/composer-field.schema.json +2 -1
  17. package/dist/codex-compat/plugins/apexrest-apex/resources/schemas/composer-instance.schema.json +1 -1
  18. package/dist/codex-compat/plugins/apexrest-apex/resources/schemas/composer-operation.schema.json +1 -1
  19. package/dist/codex-compat/plugins/apexrest-apex/resources/schemas/composer-plan.schema.json +5 -4
  20. package/dist/codex-compat/plugins/apexrest-apex/resources/schemas/deploy-plan.schema.json +18 -24
  21. package/dist/codex-compat/plugins/apexrest-apex/resources/schemas/operations.schema.json +352 -569
  22. package/dist/codex-compat/plugins/apexrest-apex/resources/schemas/policy.schema.json +21 -0
  23. package/dist/codex-compat/plugins/apexrest-apex/resources/schemas/project.schema.json +4 -7
  24. package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/README.md +0 -2
  25. package/dist/codex-compat/plugins/apexrest-apex/resources/templates/customer-crm/README.md +1 -3
  26. package/dist/codex-compat/plugins/apexrest-apex/resources/templates/existing-app/README.md +0 -2
  27. package/dist/codex-compat/plugins/apexrest-apex/runtime/apexrest.mjs +70 -44
  28. package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-5PCC7GZM.mjs +4090 -0
  29. package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-BVPAS3TD.mjs +18 -0
  30. package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-CVMECW6D.mjs +65 -0
  31. package/dist/codex-compat/plugins/apexrest-apex/runtime/{chunk-EBBEN4AV.mjs → chunk-JYN3YHP3.mjs} +4220 -10639
  32. package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-KGTE7Q5Y.mjs +23 -0
  33. package/dist/{runtime/chunk-33GE2NWZ.mjs → codex-compat/plugins/apexrest-apex/runtime/chunk-KKRFM7Q7.mjs} +21 -4
  34. package/dist/codex-compat/plugins/apexrest-apex/runtime/{chunk-2VBZESVC.mjs → chunk-MNU54A7W.mjs} +7 -1
  35. package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-N4C2SKCN.mjs +6912 -0
  36. package/dist/{runtime/chunk-KQ5IDT7Z.mjs → codex-compat/plugins/apexrest-apex/runtime/chunk-P3CVQAZW.mjs} +822 -102
  37. package/dist/{runtime/chunk-BAHECVGO.mjs → codex-compat/plugins/apexrest-apex/runtime/chunk-QKYF6N6U.mjs} +192 -114
  38. package/dist/codex-compat/plugins/apexrest-apex/runtime/{chunk-G3KR57BY.mjs → chunk-RBJJNQ5O.mjs} +7 -7
  39. package/dist/codex-compat/plugins/apexrest-apex/runtime/{chunk-YPLIIQ4Y.mjs → chunk-TMSMKVRP.mjs} +486 -352
  40. package/dist/codex-compat/plugins/apexrest-apex/runtime/{chunk-2Z3BZF66.mjs → chunk-WPS3CSQJ.mjs} +124 -11
  41. package/dist/codex-compat/plugins/apexrest-apex/runtime/mcp.mjs +5 -10
  42. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-apexlang/SKILL.md +22 -25
  43. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-apexlang/agents/openai.yaml +3 -3
  44. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-apexlang/references/component-routes.md +1 -5
  45. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-pattern-catalog/SKILL.md +4 -4
  46. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-pattern-catalog/agents/openai.yaml +1 -1
  47. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-pattern-catalog/references/capture-and-curation.md +2 -2
  48. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-safety/SKILL.md +18 -0
  49. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-safety/agents/openai.yaml +4 -0
  50. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-setup/SKILL.md +20 -10
  51. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-setup/agents/openai.yaml +3 -3
  52. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-work/SKILL.md +11 -8
  53. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-work/agents/openai.yaml +3 -3
  54. package/dist/resources/blocks/generator.json +1 -1
  55. package/dist/resources/blocks/manifest.json +6 -6
  56. package/dist/resources/blocks/packages/analytics/status-summary/1.0.0/block.yaml +1 -1
  57. package/dist/resources/blocks/packages/crud/report-dialog/1.0.0/block.yaml +1 -1
  58. package/dist/resources/blocks/packages/read/detail/1.0.0/block.yaml +1 -1
  59. package/dist/resources/blocks/packages/read/filtered-list/1.0.0/block.yaml +1 -1
  60. package/dist/resources/blocks/packages/read/history/1.0.0/block.yaml +1 -1
  61. package/dist/resources/blocks/packages/read/master-detail/1.0.0/block.yaml +1 -1
  62. package/dist/resources/schemas/composer-blueprint.schema.json +5 -4
  63. package/dist/resources/schemas/composer-contract.schema.json +2 -2
  64. package/dist/resources/schemas/composer-entity.schema.json +2 -1
  65. package/dist/resources/schemas/composer-field.schema.json +2 -1
  66. package/dist/resources/schemas/composer-instance.schema.json +1 -1
  67. package/dist/resources/schemas/composer-operation.schema.json +1 -1
  68. package/dist/resources/schemas/composer-plan.schema.json +5 -4
  69. package/dist/resources/schemas/deploy-plan.schema.json +18 -24
  70. package/dist/resources/schemas/operations.schema.json +352 -569
  71. package/dist/resources/schemas/policy.schema.json +21 -0
  72. package/dist/resources/schemas/project.schema.json +4 -7
  73. package/dist/resources/templates/blank-app/README.md +0 -2
  74. package/dist/resources/templates/customer-crm/README.md +1 -3
  75. package/dist/resources/templates/existing-app/README.md +0 -2
  76. package/dist/runtime/apexrest.mjs +70 -44
  77. package/dist/runtime/chunk-5PCC7GZM.mjs +4090 -0
  78. package/dist/runtime/chunk-BVPAS3TD.mjs +18 -0
  79. package/dist/runtime/chunk-CVMECW6D.mjs +65 -0
  80. package/dist/runtime/{chunk-EBBEN4AV.mjs → chunk-JYN3YHP3.mjs} +4220 -10639
  81. package/dist/runtime/chunk-KGTE7Q5Y.mjs +23 -0
  82. package/dist/{codex-compat/plugins/apexrest-apex/runtime/chunk-33GE2NWZ.mjs → runtime/chunk-KKRFM7Q7.mjs} +21 -4
  83. package/dist/runtime/{chunk-2VBZESVC.mjs → chunk-MNU54A7W.mjs} +7 -1
  84. package/dist/runtime/chunk-N4C2SKCN.mjs +6912 -0
  85. package/dist/{codex-compat/plugins/apexrest-apex/runtime/chunk-KQ5IDT7Z.mjs → runtime/chunk-P3CVQAZW.mjs} +822 -102
  86. package/dist/{codex-compat/plugins/apexrest-apex/runtime/chunk-BAHECVGO.mjs → runtime/chunk-QKYF6N6U.mjs} +192 -114
  87. package/dist/runtime/{chunk-G3KR57BY.mjs → chunk-RBJJNQ5O.mjs} +7 -7
  88. package/dist/runtime/{chunk-YPLIIQ4Y.mjs → chunk-TMSMKVRP.mjs} +486 -352
  89. package/dist/runtime/{chunk-2Z3BZF66.mjs → chunk-WPS3CSQJ.mjs} +124 -11
  90. package/dist/runtime/mcp.mjs +5 -10
  91. package/package.json +6 -6
  92. package/README.uk.md +0 -212
  93. package/dist/codex-compat/plugins/apexrest-apex/resources/panel/index.html +0 -68
  94. package/dist/codex-compat/plugins/apexrest-apex/resources/panel/panel.css +0 -627
  95. package/dist/codex-compat/plugins/apexrest-apex/resources/panel/panel.js +0 -861
  96. package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/README.uk.md +0 -5
  97. package/dist/codex-compat/plugins/apexrest-apex/resources/templates/customer-crm/README.uk.md +0 -7
  98. package/dist/codex-compat/plugins/apexrest-apex/resources/templates/database-control/install.sql +0 -17
  99. package/dist/codex-compat/plugins/apexrest-apex/resources/templates/existing-app/README.uk.md +0 -9
  100. package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__@playwright__test__LICENSE +0 -202
  101. package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__@playwright__test__NOTICE +0 -5
  102. package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__@types__node__LICENSE +0 -21
  103. package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__@types__yauzl__LICENSE +0 -21
  104. package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__@typescript__typescript-darwin-arm64__LICENSE +0 -55
  105. package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__@typescript__typescript-darwin-arm64__NOTICE.txt +0 -436
  106. package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__esbuild__LICENSE.md +0 -21
  107. package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__fflate__LICENSE +0 -21
  108. package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__marked__LICENSE +0 -44
  109. package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__playwright-core__LICENSE +0 -202
  110. package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__playwright-core__NOTICE +0 -5
  111. package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__playwright__LICENSE +0 -202
  112. package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__playwright__NOTICE +0 -5
  113. package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__prettier__LICENSE +0 -7
  114. package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__typescript__LICENSE +0 -55
  115. package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__typescript__NOTICE.txt +0 -436
  116. package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__undici-types__LICENSE +0 -21
  117. package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-2BBNJVKW.mjs +0 -1996
  118. package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-5JI3XEAK.mjs +0 -14
  119. package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-CYPFCUNE.mjs +0 -150
  120. package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-ESDVN7DS.mjs +0 -14
  121. package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-FTTPESNK.mjs +0 -203
  122. package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-HKJVCGQQ.mjs +0 -1302
  123. package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-RWGJITHZ.mjs +0 -992
  124. package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-TLYYCRQA.mjs +0 -329
  125. package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-WXP75UUP.mjs +0 -18
  126. package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-YQHHPRJH.mjs +0 -14
  127. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-compose/SKILL.md +0 -18
  128. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-compose/agents/openai.yaml +0 -4
  129. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-compose/references/workflow.md +0 -15
  130. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-database/SKILL.md +0 -9
  131. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-database/agents/openai.yaml +0 -4
  132. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-debug/SKILL.md +0 -9
  133. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-debug/agents/openai.yaml +0 -4
  134. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-deploy/SKILL.md +0 -21
  135. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-deploy/agents/openai.yaml +0 -4
  136. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-install-dependencies/SKILL.md +0 -38
  137. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-install-dependencies/agents/openai.yaml +0 -4
  138. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-menu/SKILL.md +0 -30
  139. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-menu/agents/openai.yaml +0 -4
  140. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-menu/references/cli-catalog.md +0 -30
  141. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-panel/SKILL.md +0 -14
  142. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-panel/agents/openai.yaml +0 -4
  143. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-project/SKILL.md +0 -11
  144. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-project/agents/openai.yaml +0 -4
  145. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-review/SKILL.md +0 -11
  146. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-review/agents/openai.yaml +0 -4
  147. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-test/SKILL.md +0 -29
  148. package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-test/agents/openai.yaml +0 -4
  149. package/dist/resources/panel/index.html +0 -68
  150. package/dist/resources/panel/panel.css +0 -627
  151. package/dist/resources/panel/panel.js +0 -861
  152. package/dist/resources/templates/blank-app/README.uk.md +0 -5
  153. package/dist/resources/templates/customer-crm/README.uk.md +0 -7
  154. package/dist/resources/templates/database-control/install.sql +0 -17
  155. package/dist/resources/templates/existing-app/README.uk.md +0 -9
  156. package/dist/resources/third-party/node_modules__@playwright__test__LICENSE +0 -202
  157. package/dist/resources/third-party/node_modules__@playwright__test__NOTICE +0 -5
  158. package/dist/resources/third-party/node_modules__@types__node__LICENSE +0 -21
  159. package/dist/resources/third-party/node_modules__@types__yauzl__LICENSE +0 -21
  160. package/dist/resources/third-party/node_modules__@typescript__typescript-darwin-arm64__LICENSE +0 -55
  161. package/dist/resources/third-party/node_modules__@typescript__typescript-darwin-arm64__NOTICE.txt +0 -436
  162. package/dist/resources/third-party/node_modules__esbuild__LICENSE.md +0 -21
  163. package/dist/resources/third-party/node_modules__fflate__LICENSE +0 -21
  164. package/dist/resources/third-party/node_modules__marked__LICENSE +0 -44
  165. package/dist/resources/third-party/node_modules__playwright-core__LICENSE +0 -202
  166. package/dist/resources/third-party/node_modules__playwright-core__NOTICE +0 -5
  167. package/dist/resources/third-party/node_modules__playwright__LICENSE +0 -202
  168. package/dist/resources/third-party/node_modules__playwright__NOTICE +0 -5
  169. package/dist/resources/third-party/node_modules__prettier__LICENSE +0 -7
  170. package/dist/resources/third-party/node_modules__typescript__LICENSE +0 -55
  171. package/dist/resources/third-party/node_modules__typescript__NOTICE.txt +0 -436
  172. package/dist/resources/third-party/node_modules__undici-types__LICENSE +0 -21
  173. package/dist/runtime/chunk-2BBNJVKW.mjs +0 -1996
  174. package/dist/runtime/chunk-5JI3XEAK.mjs +0 -14
  175. package/dist/runtime/chunk-CYPFCUNE.mjs +0 -150
  176. package/dist/runtime/chunk-ESDVN7DS.mjs +0 -14
  177. package/dist/runtime/chunk-FTTPESNK.mjs +0 -203
  178. package/dist/runtime/chunk-HKJVCGQQ.mjs +0 -1302
  179. package/dist/runtime/chunk-RWGJITHZ.mjs +0 -992
  180. package/dist/runtime/chunk-TLYYCRQA.mjs +0 -329
  181. package/dist/runtime/chunk-WXP75UUP.mjs +0 -18
  182. package/dist/runtime/chunk-YQHHPRJH.mjs +0 -14
@@ -1,992 +0,0 @@
1
- import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
2
- import {
3
- brand,
4
- clip,
5
- labelFor,
6
- padCell,
7
- paint,
8
- resultLines,
9
- resultTitle,
10
- resultTone,
11
- terminalText,
12
- valueText,
13
- wrap
14
- } from "./chunk-TLYYCRQA.mjs";
15
- import {
16
- dispatch,
17
- schemas
18
- } from "./chunk-HKJVCGQQ.mjs";
19
- import "./chunk-2BBNJVKW.mjs";
20
- import "./chunk-YPLIIQ4Y.mjs";
21
- import "./chunk-G3KR57BY.mjs";
22
- import {
23
- external_exports,
24
- resourceRoot,
25
- sqlclConfig
26
- } from "./chunk-EBBEN4AV.mjs";
27
- import {
28
- Fault,
29
- failure,
30
- sanitized
31
- } from "./chunk-2Z3BZF66.mjs";
32
-
33
- // packages/cli/src/tui.ts
34
- import { emitKeypressEvents } from "node:readline";
35
-
36
- // packages/cli/src/tui-catalog.ts
37
- var descriptions = {
38
- "dependencies.install": [
39
- "Install tools",
40
- "Install Node.js, Java, SQLcl and browser tools.",
41
- "Install tools"
42
- ],
43
- "dependencies.uninstall": [
44
- "Uninstall tools",
45
- "Remove managed tools; preserve external runtimes and saved connections.",
46
- "Uninstall tools"
47
- ],
48
- "plugin.install": [
49
- "Install plugin",
50
- "Register the APEXREST plugin in Codex using the existing runtime.",
51
- "Install plugin"
52
- ],
53
- "plugin.uninstall": [
54
- "Uninstall plugin",
55
- "Remove the APEXREST plugin registration and managed plugin files.",
56
- "Uninstall plugin"
57
- ],
58
- "connection.list": [
59
- "List saved SQLcl connections",
60
- "Browse connections saved in your local SQLcl connection store.",
61
- "List connections"
62
- ],
63
- "connection.test": [
64
- "Test saved SQLcl connection",
65
- "Choose a saved connection and read its database identity.",
66
- "Test connection"
67
- ],
68
- "sqlcl.configure": [
69
- "SQLcl settings: CLI / MCP / ORDS",
70
- "Choose SQLcl execution and direct Oracle or ORDS HTTP(S) connectivity.",
71
- "Save SQLcl settings"
72
- ]
73
- };
74
- var commands = Object.keys(descriptions).map((operation) => ({
75
- operation,
76
- command: operation.replace(".", " "),
77
- label: descriptions[operation][0],
78
- description: descriptions[operation][1],
79
- action: descriptions[operation][2]
80
- }));
81
- var labels = {
82
- mode: "SQLcl execution mode",
83
- databaseTransport: "Database network transport",
84
- mcpRestrictLevel: "MCP restrict level",
85
- home: "Managed tools directory",
86
- offline: "Use cached downloads only",
87
- cacheDir: "Download cache directory",
88
- acceptOracleLicense: "Accept Oracle license terms",
89
- skipBrowser: "Skip browser installation",
90
- installOsDeps: "Install browser system packages",
91
- from: "Package source directory",
92
- codexHome: "Codex profile directory",
93
- keepRuntime: "Keep plugin files"
94
- };
95
- var hints = {
96
- mode: "CLI runs SQLcl directly. MCP uses the official sql -mcp server. Applies to new operations.",
97
- databaseTransport: "direct: Oracle listener. ords: HTTP(S) through ORDS, with plugin connection settings; requires CLI.",
98
- mcpRestrictLevel: "4: Oracle default restrictions. 1: allow scripts, block host commands. Applies only to MCP.",
99
- home: "Optional directory for managed APEXREST tools and installation records.",
100
- acceptOracleLicense: "Enable only after accepting the Oracle terms linked on the review screen.",
101
- installOsDeps: "Explicit permission for browser operating-system package installation.",
102
- offline: "Use previously cached artifacts; fail if a required download is missing.",
103
- cacheDir: "Optional directory containing cached toolchain downloads.",
104
- skipBrowser: "Skip Chromium and browser tooling.",
105
- from: "Optional local APEXREST package; defaults to the package running this menu.",
106
- codexHome: "Optional Codex profile directory; defaults to your active user profile.",
107
- keepRuntime: "Keep plugin files after removing registration. Shared tools are preserved."
108
- };
109
- var visibleFields = {
110
- "dependencies.install": [
111
- "acceptOracleLicense",
112
- "skipBrowser",
113
- "home",
114
- "cacheDir",
115
- "offline",
116
- "installOsDeps"
117
- ],
118
- "dependencies.uninstall": ["home"],
119
- "plugin.install": ["from", "home", "codexHome"],
120
- "plugin.uninstall": ["keepRuntime", "home"],
121
- "connection.list": [],
122
- "connection.test": [],
123
- "sqlcl.configure": ["mode", "databaseTransport", "mcpRestrictLevel"]
124
- };
125
- var advancedFields = /* @__PURE__ */ new Set([
126
- "mcpRestrictLevel",
127
- "home",
128
- "cacheDir",
129
- "codexHome",
130
- "offline",
131
- "installOsDeps",
132
- "keepRuntime",
133
- "from"
134
- ]);
135
- function fieldsFor(operation, _project = "") {
136
- const schema = external_exports.toJSONSchema(schemas[operation], { io: "input" });
137
- return (visibleFields[operation] ?? []).map((name) => {
138
- const property = schema.properties?.[name];
139
- if (!property || typeof property === "boolean") throw new Error(`Unsupported field schema: ${name}`);
140
- return {
141
- name,
142
- label: labels[name] ?? name,
143
- type: String(property.type ?? "string"),
144
- required: schema.required?.includes(name) ?? false,
145
- initial: property.default === void 0 ? "" : String(property.default),
146
- choices: property.enum?.map(String) ?? [],
147
- hint: hints[name] ?? "",
148
- advanced: advancedFields.has(name)
149
- };
150
- });
151
- }
152
- function parseFields(operation, fields, values) {
153
- const input = operation === "plugin.install" ? { nativeOnly: true } : {};
154
- if (["dependencies.install", "dependencies.uninstall", "plugin.install"].includes(operation)) {
155
- input.dryRun = false;
156
- input.yes = true;
157
- }
158
- if (operation.startsWith("connection.")) input.saved = true;
159
- for (const field of fields) {
160
- const value = values[field.name] ?? "";
161
- if (value === "") continue;
162
- input[field.name] = field.type === "boolean" ? value === "true" ? true : value === "false" ? false : value : value;
163
- }
164
- return schemas[operation].safeParse(input);
165
- }
166
- function commandPreview(operation, input) {
167
- const quote = (value) => /^[a-zA-Z0-9_./:@=-]+$/.test(value) ? value : `'${value.replaceAll("'", "'\\''")}'`;
168
- const args = ["apexrest", ...operation.split(".")];
169
- for (const [name, value] of Object.entries(input)) {
170
- if (value === false || value === void 0) continue;
171
- args.push("--" + name.replace(/[A-Z]/g, (c) => "-" + c.toLowerCase()));
172
- if (value !== true) args.push(quote(String(value)));
173
- }
174
- return args.join(" ");
175
- }
176
-
177
- // packages/cli/src/tui-apexlang.ts
178
- import path from "node:path";
179
- import { readFile } from "node:fs/promises";
180
- var groups = /* @__PURE__ */ new Map([
181
- ["items", "Page items"],
182
- ["region-components", "Regions"],
183
- ["buttons", "Buttons"],
184
- ["business-logic", "Business logic"],
185
- ["shared-components", "Shared components"],
186
- ["template-components", "Template components"],
187
- ["page-layout-templates", "Page layouts"],
188
- ["page-examples", "Page examples"],
189
- ["workspace-components", "Workspace components"]
190
- ]);
191
- var displayName = (name) => labelFor(name).replace(
192
- /\b(acl|ai|app|lovs?|rest)\b/gi,
193
- (word) => ({ acl: "ACL", ai: "AI", app: "Application", lov: "LOV", lovs: "LOVs", rest: "REST" })[word.toLowerCase()]
194
- );
195
- function apexlangCatalogue(records) {
196
- if (!Array.isArray(records)) throw new Error("Invalid APEXlang catalogue");
197
- const families = /* @__PURE__ */ new Map();
198
- const versions = /* @__PURE__ */ new Set();
199
- for (const record of records) {
200
- if (!record || typeof record !== "object") throw new Error("Invalid APEXlang record");
201
- if (!["template", "contract", "guide"].includes(record.kind)) continue;
202
- if (typeof record.family !== "string") continue;
203
- const [root, name] = record.family.split("/");
204
- const group = groups.get(root);
205
- if (!group || root !== "buttons" && (!name || name.startsWith("_"))) continue;
206
- if (typeof record.id !== "string" || typeof record.version !== "string")
207
- throw new Error("Invalid APEXlang component metadata");
208
- const family = root === "buttons" ? root : `${root}/${name}`;
209
- let entry = families.get(family);
210
- if (!entry) {
211
- entry = {
212
- item: { family, group, name: displayName(name ?? root), documents: 0 },
213
- ids: /* @__PURE__ */ new Set()
214
- };
215
- families.set(family, entry);
216
- }
217
- entry.ids.add(record.id);
218
- versions.add(record.version);
219
- }
220
- if (!families.size) throw new Error("Empty APEXlang catalogue");
221
- const order = [...groups.values()];
222
- return {
223
- version: [...versions].sort().join(", "),
224
- items: [...families.values()].map(({ item, ids }) => ({ ...item, documents: ids.size })).sort((a, b) => order.indexOf(a.group) - order.indexOf(b.group) || a.name.localeCompare(b.name, "en"))
225
- };
226
- }
227
- async function loadApexlangCatalogue(file = path.join(resourceRoot(), "references/index.json")) {
228
- return apexlangCatalogue(JSON.parse(await readFile(file, "utf8")));
229
- }
230
- function filterApexlang(catalogue, query) {
231
- const words = query.toLowerCase().trim().split(/\s+/);
232
- return (catalogue?.items ?? []).filter((item) => {
233
- const text = `${item.name} ${item.group} ${item.family}`.toLowerCase();
234
- return words.every((word) => text.includes(word));
235
- });
236
- }
237
-
238
- // packages/cli/src/tui-home.ts
239
- function startAt(selected, length, room) {
240
- return Math.min(Math.max(0, selected - Math.floor(room / 2)), Math.max(0, length - room));
241
- }
242
- function homeFrame(state) {
243
- const { width, height, color, focus } = state;
244
- const styled = (text, tone = "plain", size = width) => paint(clip(text, size), tone, color);
245
- const header = brand(color, width < 62 || height < 28);
246
- if (height >= 18)
247
- header.push(
248
- styled(
249
- ` SQLcl: ${state.sqlclMode ?? "Loading\u2026"}${state.sqlclMode ? " \xB7 " + (state.databaseTransport === "ords" ? "ORDS HTTP(S)" : "Direct Oracle") : ""}`,
250
- "muted"
251
- )
252
- );
253
- if (height >= 18) header.push("");
254
- header.push(
255
- styled(
256
- focus === "apexlang" ? state.catalogueQuery ? ` Find APEXlang: ${state.catalogueQuery}` : " Browse supported APEXlang components" : state.query ? ` Search: ${state.query}` : " What would you like to do?",
257
- "accent"
258
- )
259
- );
260
- if (height >= 18) header.push("");
261
- const room = height - header.length - 2;
262
- const actionRows = (size, count) => {
263
- const start = startAt(state.selected, state.actions.length, count);
264
- const rows = state.actions.slice(start, start + count).map(
265
- (action, index) => styled(
266
- `${focus === "actions" && start + index === state.selected ? "> " : " "}${action.label}`,
267
- focus === "actions" && start + index === state.selected ? "selected" : "plain",
268
- size
269
- )
270
- );
271
- if (!state.actions.length) rows.push(styled(" No matching actions. Esc clears search.", "muted", size));
272
- while (rows.length < count) rows.push("");
273
- return rows;
274
- };
275
- const catalogueRows = (size, count) => {
276
- const active = focus === "apexlang";
277
- const catalogue = state.catalogue;
278
- const rows = [
279
- styled(
280
- ` ${active ? "\u25CF" : "\u25CB"} APEXlang${catalogue ? ` \xB7 ${catalogue.items.length} types \xB7 ${new Set(catalogue.items.map((item) => item.group)).size} groups` : ""}`,
281
- active ? "accent" : "muted",
282
- size
283
- )
284
- ];
285
- if (state.catalogueError || !catalogue) {
286
- rows.push(
287
- styled(
288
- state.catalogueError ? " Catalogue unavailable." : " Loading bundled catalogue\u2026",
289
- "muted",
290
- size
291
- )
292
- );
293
- if (state.catalogueError && count >= 3)
294
- rows.push(styled(" Ctrl+R retries the local file.", "muted", size));
295
- } else {
296
- const items = filterApexlang(catalogue, state.catalogueQuery);
297
- const groupWidth = size >= 55 ? 23 : 0;
298
- const nameWidth = size - groupWidth - 10;
299
- const tableRow = (name, group, docs, marker = " ") => marker + padCell(name, nameWidth) + " \u2502 " + (groupWidth ? padCell(group, groupWidth - 3) + " \u2502 " : "") + docs.padStart(4);
300
- const table = count >= 5;
301
- if (table) rows.push(styled(tableRow("Item / component", "Group", "Docs"), "muted", size));
302
- const available = Math.max(1, count - (table ? 4 : 1));
303
- const selected = Math.max(0, Math.min(state.catalogueSelected, items.length - 1));
304
- const start = startAt(selected, items.length, available);
305
- for (const [offset, item] of items.slice(start, start + available).entries()) {
306
- const selectedRow = active && start + offset === selected;
307
- rows.push(
308
- styled(
309
- table ? tableRow(item.name, item.group, String(item.documents), selectedRow ? "> " : " ") : ` ${selectedRow ? ">" : " "} ${item.name}`,
310
- selectedRow ? "selected" : "plain",
311
- size
312
- )
313
- );
314
- }
315
- if (!items.length) rows.push(styled(" No matching components.", "muted", size));
316
- if (table) {
317
- while (rows.length < count - 2) rows.push("");
318
- rows.push(
319
- styled(
320
- ` ${items.length ? `${start + 1}\u2013${Math.min(start + available, items.length)}` : "0"} / ${items.length} \xB7 ${groupWidth ? catalogue.version : items[selected]?.group ?? catalogue.version}`,
321
- "muted",
322
- size
323
- )
324
- );
325
- rows.push(styled(" Bundled Oracle refs \xB7 validate locally", "muted", size));
326
- }
327
- }
328
- while (rows.length < count) rows.push("");
329
- return rows.slice(0, count);
330
- };
331
- let body;
332
- if (width >= 108) {
333
- const leftWidth = 38, rightWidth = width - leftWidth - 3;
334
- const left = actionRows(leftWidth, room), right = catalogueRows(rightWidth, room);
335
- body = left.map(
336
- (line, index) => line + " ".repeat(leftWidth - clip(line, leftWidth).length) + styled(" \u2502 ", "muted") + right[index]
337
- );
338
- } else if (focus === "apexlang") {
339
- body = catalogueRows(width, room);
340
- } else {
341
- const actionCount = Math.min(Math.max(1, state.actions.length), Math.max(1, room - 2));
342
- const gap = room - actionCount >= 5 ? 1 : 0;
343
- body = [
344
- ...actionRows(width, actionCount),
345
- ...gap ? [""] : [],
346
- ...catalogueRows(width, room - actionCount - gap)
347
- ];
348
- }
349
- const footer = width < 44 ? focus === "apexlang" ? " Tab Actions \u2191\u2193 Type Esc" : " Tab APEXlang \u2191\u2193 Enter Esc" : width < 64 ? focus === "apexlang" ? " Tab Actions \u2191\u2193 Scroll Type Search Esc" : " Tab APEXlang \u2191\u2193 Enter Type Search Esc" : focus === "apexlang" ? " Tab Actions \u2191\u2193 PgUp/PgDn Type Search Esc Back" : " Tab APEXlang \u2191\u2193 Move Enter Open Type Search Esc Quit";
350
- return [...header, ...body, styled("\u2500".repeat(width), "muted"), styled(footer, "muted")].slice(0, height);
351
- }
352
-
353
- // packages/cli/src/tui.ts
354
- async function runTui({
355
- project = process.cwd(),
356
- input = process.stdin,
357
- output = process.stdout,
358
- execute = dispatch,
359
- loadCatalogue = loadApexlangCatalogue,
360
- loadSqlcl = sqlclConfig
361
- } = {}) {
362
- if (!input.isTTY || !output.isTTY || process.env.TERM === "dumb")
363
- throw new Fault(
364
- "TTY_REQUIRED",
365
- "The TUI requires an interactive terminal. Use apexrest --help for commands.",
366
- 2
367
- );
368
- let screen = "menu";
369
- let query = "", selected = 0, fieldIndex = 0, scroll = 0, choiceIndex = 0;
370
- let homeFocus = "actions";
371
- let catalogue;
372
- let catalogueQuery = "", catalogueSelected = 0, catalogueError = false, catalogueLoading = false;
373
- let command = commands[0];
374
- let sqlcl, sqlclError = false, sqlclRevision = 0;
375
- let fields = [], values = {}, parsed = {};
376
- let error = "", editing = "", cursor = 0, result, editField;
377
- let advanced = false, details = false;
378
- let controller, started = 0, closeAfterRun = false, closed = false;
379
- let finish, reject;
380
- const done = new Promise((resolve, fail) => {
381
- finish = resolve;
382
- reject = fail;
383
- });
384
- const crash = (error2) => {
385
- closed = true;
386
- reject(error2);
387
- };
388
- const drafts = /* @__PURE__ */ new Map();
389
- let savedNames = [], connectionQuery = "";
390
- const visibleConnections = () => savedNames.filter((name) => name.toLowerCase().includes(connectionQuery.toLowerCase()));
391
- const filtered = () => commands.filter(
392
- (item) => `${item.label} ${item.command} ${item.description}`.toLowerCase().includes(query.toLowerCase())
393
- );
394
- async function reloadCatalogue() {
395
- if (catalogueLoading) return;
396
- catalogueLoading = true;
397
- catalogueError = false;
398
- try {
399
- catalogue = await loadCatalogue();
400
- } catch {
401
- catalogue = void 0;
402
- catalogueError = true;
403
- } finally {
404
- catalogueLoading = false;
405
- if (screen === "menu") renderSafe();
406
- }
407
- }
408
- const formRows = () => [
409
- ...fields.filter((field) => !field.advanced).map((field) => ({ kind: "field", field })),
410
- ...fields.some((field) => field.advanced) ? [{ kind: "advanced" }] : [],
411
- ...advanced ? fields.filter((field) => field.advanced).map((field) => ({ kind: "field", field })) : [],
412
- { kind: "review" }
413
- ];
414
- const choices = () => {
415
- const available = command.operation === "sqlcl.configure" && editField.name === "mode" && values.databaseTransport === "ords" ? editField.choices.filter((choice) => choice === "cli") : editField.choices;
416
- return editField.required || editField.name === "databaseTransport" ? available : ["", ...available];
417
- };
418
- const close = () => {
419
- closed = true;
420
- finish();
421
- };
422
- const cancel = (exitAfter = false) => {
423
- if (screen === "running") {
424
- closeAfterRun ||= exitAfter;
425
- controller?.abort();
426
- render();
427
- } else close();
428
- };
429
- const fieldValue = (field) => {
430
- const value = values[field.name] ?? "";
431
- return field.type === "boolean" ? value === "true" ? "[x] Yes" : "[ ] No" : value || (field.required ? "Enter a value" : "Default");
432
- };
433
- const stage = (step) => ["Configure", "Review", "Result"].map((name, index) => index + 1 === step ? `[${name}]` : name).join(" \u203A ");
434
- function render() {
435
- if (closed) return;
436
- const width = Math.max(1, (output.columns || 80) - 2), height = Math.max(1, (output.rows || 24) - 1);
437
- if (width < 28 || height < 9) {
438
- output.write("\x1B[H\x1B[2J" + clip("Resize terminal (30x10 minimum). Ctrl+C exits.", width));
439
- return;
440
- }
441
- const color = !("NO_COLOR" in process.env);
442
- if (screen === "menu") {
443
- const actions = filtered();
444
- selected = Math.max(0, Math.min(selected, actions.length - 1));
445
- output.write(
446
- "\x1B[H\x1B[2J" + homeFrame({
447
- width,
448
- height,
449
- color,
450
- actions,
451
- selected,
452
- query,
453
- focus: homeFocus,
454
- catalogue,
455
- catalogueError,
456
- catalogueQuery,
457
- catalogueSelected,
458
- sqlclMode: sqlclError ? "Configuration unavailable" : sqlcl?.mode.toUpperCase(),
459
- databaseTransport: sqlcl?.databaseTransport
460
- }).join("\r\n")
461
- );
462
- return;
463
- }
464
- const styled = (text, tone = "plain") => paint(clip(text, width), tone, color);
465
- const header = [];
466
- let body = [], footer = "", focused = -1;
467
- const heading = (title, step) => {
468
- header.push(styled(` APEXREST / ${title}`, "accent"));
469
- if (step && height >= 16) header.push(styled(` ${stage(step)}`, "muted"));
470
- header.push("");
471
- };
472
- const row = (text, index, active) => ({
473
- text: `${index === active ? "> " : " "}${text}`,
474
- tone: index === active ? "selected" : "plain"
475
- });
476
- if (screen === "connections") {
477
- heading("Saved SQLcl connections");
478
- header.push(
479
- styled(
480
- connectionQuery ? ` Search: ${connectionQuery}` : ` ${savedNames.length} saved connection${savedNames.length === 1 ? "" : "s"}`,
481
- "muted"
482
- ),
483
- ""
484
- );
485
- const names = visibleConnections();
486
- selected = Math.max(0, Math.min(selected, names.length - 1));
487
- body = names.map((name, index) => row(name, index, selected));
488
- if (!savedNames.length)
489
- body = [
490
- { text: "No saved SQLcl connections found.", tone: "muted" },
491
- { text: "Save a connection in SQLcl, then press Ctrl+R to refresh." }
492
- ];
493
- else if (!names.length)
494
- body.push({ text: "No matching connections. Esc clears the search.", tone: "muted" });
495
- focused = names.length ? selected : -1;
496
- footer = width >= 64 ? " \u2191\u2193 Move Enter Test Type to search ^R Refresh Esc Back" : " \u2191\u2193 Enter Test Type Search ^R Refresh Esc";
497
- } else if (screen === "form" || screen === "edit") {
498
- heading(command.label, 1);
499
- if (error) header.push(styled(` ${error}`, "error"));
500
- else if (height >= 17) header.push(styled(` ${command.description}`, "muted"));
501
- if (height >= 17) header.push("");
502
- const rows = formRows();
503
- fieldIndex = Math.min(fieldIndex, rows.length - 1);
504
- body = rows.map((item, index) => {
505
- if (item.kind === "advanced")
506
- return row(
507
- `${advanced ? "[-]" : "[+]"} Advanced options (${fields.filter((field2) => field2.advanced).length})`,
508
- index,
509
- fieldIndex
510
- );
511
- if (item.kind === "review") return row("[ Review & run ]", index, fieldIndex);
512
- const field = item.field;
513
- const labelWidth = Math.min(28, Math.max(7, Math.floor((width - 7) / 2)));
514
- const label = field.label + (field.required ? " *" : "");
515
- const shortLabel = label.length > labelWidth ? clip(label, labelWidth - 1) + "\u2026" : label;
516
- return row(`${shortLabel.padEnd(labelWidth)} ${fieldValue(field)}`, index, fieldIndex);
517
- });
518
- focused = fieldIndex;
519
- const selectedRow = rows[fieldIndex];
520
- if (screen === "edit") {
521
- const chars = Array.from(editing);
522
- const editorLabel = width < 54 ? "" : clip(editField.label, Math.floor(width / 3)) + ": ";
523
- const room = Math.max(5, width - editorLabel.length - 4);
524
- const start = Math.max(0, cursor - Math.floor(room / 2));
525
- body[fieldIndex] = {
526
- text: `> ${editorLabel}${start ? "\u2026" : ""}${chars.slice(start, cursor).join("")}\u2502${chars.slice(cursor).join("")}`,
527
- tone: "selected"
528
- };
529
- header.push(styled(` ${width < 54 ? editField.label : editField.hint}`, "muted"));
530
- footer = width >= 64 ? " Enter Save & next Esc Cancel Ctrl+U Clear" : " Enter Save Esc Cancel Ctrl+U Clear";
531
- } else {
532
- if (height >= 17 && selectedRow?.kind === "field")
533
- header.push(styled(` ${selectedRow.field.hint}`, "muted"));
534
- footer = width >= 68 ? " Enter Edit Ctrl+R Review Ctrl+O Advanced Esc Back" : " Enter Edit ^R Review ^O More Esc Back";
535
- }
536
- } else if (screen === "choices") {
537
- heading(command.label, 1);
538
- header.push(styled(` ${editField.label}`, "accent"));
539
- if (height >= 17) header.push(styled(` ${editField.hint}`, "muted"));
540
- header.push("");
541
- body = choices().map((value, index) => row(value || "Use default", index, choiceIndex));
542
- focused = choiceIndex;
543
- footer = " \u2191\u2193 Move Enter Save & next Esc Cancel";
544
- } else if (screen === "review") {
545
- heading(command.label, 2);
546
- header.push(styled(" Ready to run", "accent"), "");
547
- if (details) {
548
- body = [
549
- { text: commandPreview(command.operation, parsed) },
550
- { text: "" },
551
- { text: JSON.stringify(sanitized(parsed), null, 2) }
552
- ];
553
- } else {
554
- body.push({ text: command.description, tone: "muted" }, { text: "" });
555
- for (const field of fields) {
556
- const value = parsed[field.name];
557
- if (value === void 0 || value === "" || field.name === "project") continue;
558
- if (field.advanced && values[field.name] === field.initial) continue;
559
- body.push({ text: ` ${field.label}: ${valueText(value)}` });
560
- }
561
- if (command.operation === "dependencies.install")
562
- body.push(
563
- { text: "" },
564
- {
565
- text: "Oracle terms: https://www.oracle.com/downloads/licenses/oracle-free-license.html",
566
- tone: "muted"
567
- }
568
- );
569
- if (command.operation === "sqlcl.configure")
570
- body.push(
571
- { text: "" },
572
- {
573
- text: "Saved for new Oracle operations in this APEXREST home. Current operations keep their mode."
574
- },
575
- ...parsed.databaseTransport === "ords" ? [
576
- {
577
- text: "ORDS requires CLI execution. Configure each reference with connection add --ords-url URL --ords-username USER --password-file PATH.",
578
- tone: "muted"
579
- }
580
- ] : [],
581
- ...parsed.mode === "mcp" ? [
582
- {
583
- text: "Oracle MCP may write DBTOOLS$MCP_LOG during connected operations.",
584
- tone: "muted"
585
- }
586
- ] : []
587
- );
588
- body.push({ text: "" }, { text: `> [ ${command.action} ]`, tone: "selected" });
589
- }
590
- footer = width >= 64 ? " Enter Run Esc Edit D Details \u2191\u2193 Scroll" : " Enter Run Esc Edit D Details \u2191\u2193 Scroll";
591
- } else if (screen === "running") {
592
- heading(command.label);
593
- const elapsed = Math.floor((Date.now() - started) / 1e3);
594
- const spinner = ["\u25D0", "\u25D3", "\u25D1", "\u25D2"][Math.floor(Date.now() / 250) % 4];
595
- header.push(
596
- styled(
597
- ` ${spinner} ${controller?.signal.aborted ? "Cancelling" : "Running"} \xB7 ${elapsed}s`,
598
- "accent"
599
- ),
600
- ""
601
- );
602
- body = [
603
- {
604
- text: controller?.signal.aborted ? "Cancellation requested. Waiting for the actual outcome; some steps cannot be interrupted." : command.description
605
- }
606
- ];
607
- footer = " Ctrl+C Request cancellation";
608
- } else {
609
- heading(command.label, command.operation.startsWith("connection.") ? void 0 : 3);
610
- header.push(styled(` ${resultTitle(result)}`, resultTone(result)), "");
611
- body = details ? [{ text: JSON.stringify(sanitized(result), null, 2) }] : resultLines(result);
612
- footer = command.operation.startsWith("connection.") ? " Enter Connections Esc Menu D Details \u2191\u2193 Scroll" : width >= 68 ? " Enter Edit Esc Menu D Details \u2191\u2193 / PgUp PgDn Scroll" : " Enter Edit Esc Menu D Details \u2191\u2193 Scroll";
613
- }
614
- if (width < 44)
615
- footer = screen === "form" ? " Enter ^R Run ^O More Esc" : screen === "edit" ? " Enter Save Esc Cancel" : screen === "choices" ? " \u2191\u2193 Enter Save Esc Cancel" : screen === "running" ? " Ctrl+C Cancel" : " Enter Esc D Details \u2191\u2193";
616
- const available = Math.max(1, height - header.length - 2);
617
- if (focused < 0)
618
- body = body.flatMap((line) => wrap(line.text, width - 1).map((text) => ({ ...line, text })));
619
- if (focused >= 0) scroll = Math.max(0, focused - Math.floor(available / 2));
620
- scroll = Math.min(scroll, Math.max(0, body.length - available));
621
- const visible = body.slice(scroll, scroll + available).map((line) => styled(line.text, line.tone));
622
- while (visible.length < available) visible.push("");
623
- const position = body.length > available ? ` ${scroll + 1}\u2013${Math.min(scroll + available, body.length)} / ${body.length} ` : "";
624
- const divider = position + "\u2500".repeat(Math.max(0, width - position.length));
625
- const frame = [...header, ...visible, styled(divider, "muted"), styled(footer, "muted")].slice(0, height);
626
- output.write("\x1B[H\x1B[2J" + frame.join("\r\n"));
627
- }
628
- function review() {
629
- const validated = parseFields(command.operation, fields, values);
630
- if (!validated.success) {
631
- const issue = validated.error.issues[0];
632
- const field = fields.find((field2) => field2.name === issue.path[0]);
633
- error = field && !(values[field.name] ?? "").trim() ? `Enter ${field.label.toLowerCase()}.` : `${field?.label ?? "Input"}: ${issue.message}`;
634
- if (field?.advanced) advanced = true;
635
- const first = formRows().findIndex((item) => item.kind === "field" && item.field.name === field?.name);
636
- if (first >= 0) fieldIndex = first;
637
- screen = "form";
638
- } else {
639
- parsed = validated.data;
640
- screen = "review";
641
- scroll = 0;
642
- error = "";
643
- details = false;
644
- }
645
- }
646
- function open(item) {
647
- command = item;
648
- fields = fieldsFor(command.operation, project).sort((a, b) => Number(b.required) - Number(a.required));
649
- values = drafts.get(command.operation) ?? Object.fromEntries(fields.map((field) => [field.name, field.initial]));
650
- drafts.set(command.operation, values);
651
- fieldIndex = 0;
652
- scroll = 0;
653
- error = "";
654
- advanced = false;
655
- details = false;
656
- screen = "form";
657
- if (command.operation === "sqlcl.configure") {
658
- void loadSqlclSettings(true).catch(crash);
659
- return;
660
- }
661
- if (command.operation.startsWith("connection.")) {
662
- void loadConnections().catch(crash);
663
- return;
664
- }
665
- if (!fields.some((field) => !field.advanced)) review();
666
- }
667
- async function loadSqlclSettings(edit = false) {
668
- const revision = ++sqlclRevision;
669
- if (edit) {
670
- screen = "running";
671
- started = Date.now();
672
- controller = new AbortController();
673
- render();
674
- }
675
- try {
676
- const loaded = await loadSqlcl();
677
- if (revision !== sqlclRevision) return;
678
- sqlcl = loaded;
679
- sqlclError = false;
680
- if (edit) {
681
- values = {
682
- mode: sqlcl.mode,
683
- databaseTransport: sqlcl.databaseTransport ?? "direct",
684
- mcpRestrictLevel: sqlcl.mcpRestrictLevel
685
- };
686
- drafts.set(command.operation, values);
687
- screen = "form";
688
- }
689
- } catch (error2) {
690
- if (revision !== sqlclRevision) return;
691
- sqlclError = true;
692
- if (edit) {
693
- result = failure("sqlcl.status", error2);
694
- screen = "result";
695
- }
696
- }
697
- if (edit && closeAfterRun) close();
698
- else renderSafe();
699
- }
700
- function nextField() {
701
- error = "";
702
- screen = "form";
703
- fieldIndex = Math.min(formRows().length - 1, fieldIndex + 1);
704
- }
705
- function toggleAdvanced() {
706
- advanced = !advanced;
707
- const index = formRows().findIndex((row) => row.kind === "advanced");
708
- if (index >= 0) fieldIndex = index;
709
- }
710
- async function loadConnections() {
711
- screen = "running";
712
- scroll = 0;
713
- started = Date.now();
714
- controller = new AbortController();
715
- render();
716
- try {
717
- result = await execute("connection.list", { saved: true }, controller.signal);
718
- if (result.ok) {
719
- const data = result.data;
720
- if (!Array.isArray(data?.connections) || !data.connections.every((item) => typeof item.name === "string"))
721
- throw new Fault(
722
- "CONNECTION_LIST_UNCONFIRMED",
723
- "SQLcl did not return a valid saved connection list.",
724
- 3
725
- );
726
- savedNames = data.connections.map((item) => item.name);
727
- screen = "connections";
728
- selected = 0;
729
- } else screen = "result";
730
- } catch (error2) {
731
- result = failure("connection.list", error2);
732
- screen = "result";
733
- }
734
- details = false;
735
- if (closeAfterRun) close();
736
- else render();
737
- }
738
- async function run() {
739
- screen = "running";
740
- scroll = 0;
741
- started = Date.now();
742
- controller = new AbortController();
743
- render();
744
- try {
745
- result = await execute(command.operation, parsed, controller.signal);
746
- if (result.ok && command.operation === "sqlcl.configure") {
747
- sqlclRevision++;
748
- sqlcl = result.data;
749
- sqlclError = false;
750
- }
751
- } catch (e) {
752
- result = failure(command.operation, e);
753
- }
754
- screen = "result";
755
- scroll = 0;
756
- details = false;
757
- if (closeAfterRun) close();
758
- else render();
759
- }
760
- function onKey(text, key = {}) {
761
- if (closed) return;
762
- if (key.ctrl && key.name === "c") {
763
- cancel();
764
- return;
765
- }
766
- if (screen === "running") return;
767
- const up = key.name === "up", down = key.name === "down", enter = key.name === "return", escape = key.name === "escape";
768
- if (screen === "menu" && key.name === "tab") {
769
- homeFocus = homeFocus === "actions" ? "apexlang" : "actions";
770
- render();
771
- return;
772
- }
773
- if (screen === "menu" && key.ctrl && key.name === "r") {
774
- void reloadCatalogue().catch(crash);
775
- render();
776
- return;
777
- }
778
- if (screen === "menu" && homeFocus === "apexlang") {
779
- const length = filterApexlang(catalogue, catalogueQuery).length;
780
- if (up) catalogueSelected = Math.max(0, catalogueSelected - 1);
781
- else if (down) catalogueSelected = Math.min(Math.max(0, length - 1), catalogueSelected + 1);
782
- else if (key.name === "pageup") catalogueSelected = Math.max(0, catalogueSelected - 8);
783
- else if (key.name === "pagedown")
784
- catalogueSelected = Math.min(Math.max(0, length - 1), catalogueSelected + 8);
785
- else if (key.name === "home") catalogueSelected = 0;
786
- else if (key.name === "end") catalogueSelected = Math.max(0, length - 1);
787
- else if (escape) {
788
- if (catalogueQuery) {
789
- catalogueQuery = "";
790
- catalogueSelected = 0;
791
- } else homeFocus = "actions";
792
- } else if (key.name === "backspace") {
793
- catalogueQuery = Array.from(catalogueQuery).slice(0, -1).join("");
794
- catalogueSelected = 0;
795
- } else if (text && !key.ctrl && !key.meta && !enter) {
796
- catalogueQuery = (catalogueQuery + terminalText(text)).slice(0, 256);
797
- catalogueSelected = 0;
798
- }
799
- render();
800
- return;
801
- }
802
- if (screen === "menu" || screen === "connections") {
803
- const connectionsScreen = screen === "connections";
804
- const length = connectionsScreen ? visibleConnections().length : filtered().length;
805
- if (up) selected = Math.max(0, selected - 1);
806
- else if (down) selected = Math.min(Math.max(0, length - 1), selected + 1);
807
- else if (key.name === "pageup") selected = Math.max(0, selected - 8);
808
- else if (key.name === "pagedown") selected = Math.min(Math.max(0, length - 1), selected + 8);
809
- else if (key.name === "home") selected = 0;
810
- else if (key.name === "end") selected = Math.max(0, length - 1);
811
- else if (connectionsScreen && key.ctrl && key.name === "r") {
812
- void loadConnections().catch(crash);
813
- return;
814
- } else if (enter && length) {
815
- if (connectionsScreen) {
816
- command = commands.find((item) => item.operation === "connection.test");
817
- parsed = { saved: true, name: visibleConnections()[selected] };
818
- void run().catch(crash);
819
- return;
820
- }
821
- open(filtered()[selected]);
822
- } else if (escape) {
823
- if (connectionsScreen) {
824
- if (connectionQuery) connectionQuery = "";
825
- else {
826
- screen = "menu";
827
- selected = 0;
828
- }
829
- } else if (query) {
830
- query = "";
831
- selected = 0;
832
- } else close();
833
- } else if (key.name === "backspace") {
834
- if (connectionsScreen) connectionQuery = Array.from(connectionQuery).slice(0, -1).join("");
835
- else query = Array.from(query).slice(0, -1).join("");
836
- selected = 0;
837
- } else if (text && !key.ctrl && !key.meta) {
838
- if (connectionsScreen) connectionQuery = (connectionQuery + terminalText(text)).slice(0, 512);
839
- else query = (query + terminalText(text)).slice(0, 256);
840
- selected = 0;
841
- }
842
- } else if (screen === "form") {
843
- const rows = formRows(), item = rows[fieldIndex];
844
- if (up || key.name === "tab" && key.shift) fieldIndex = Math.max(0, fieldIndex - 1);
845
- else if (down || key.name === "tab") fieldIndex = Math.min(rows.length - 1, fieldIndex + 1);
846
- else if (escape) {
847
- screen = "menu";
848
- scroll = 0;
849
- } else if (key.ctrl && key.name === "r") review();
850
- else if (key.ctrl && key.name === "o") toggleAdvanced();
851
- else if (enter || key.name === "space" && item.kind === "field" && item.field.type === "boolean") {
852
- if (item.kind === "review") review();
853
- else if (item.kind === "advanced") toggleAdvanced();
854
- else {
855
- editField = item.field;
856
- if (editField.type === "boolean")
857
- values[editField.name] = values[editField.name] === "true" ? "false" : "true";
858
- else if (editField.choices.length) {
859
- choiceIndex = Math.max(0, choices().indexOf(values[editField.name] ?? ""));
860
- screen = "choices";
861
- } else {
862
- editing = values[editField.name] ?? "";
863
- cursor = Array.from(editing).length;
864
- screen = "edit";
865
- }
866
- error = "";
867
- }
868
- }
869
- } else if (screen === "choices") {
870
- if (up) choiceIndex = Math.max(0, choiceIndex - 1);
871
- else if (down) choiceIndex = Math.min(choices().length - 1, choiceIndex + 1);
872
- else if (enter) {
873
- values[editField.name] = choices()[choiceIndex];
874
- if (command.operation === "sqlcl.configure" && editField.name === "databaseTransport" && values.databaseTransport === "ords")
875
- values.mode = "cli";
876
- nextField();
877
- } else if (escape) screen = "form";
878
- } else if (screen === "edit") {
879
- const chars = Array.from(editing);
880
- if (escape) screen = "form";
881
- else if (enter) {
882
- values[editField.name] = editing;
883
- nextField();
884
- } else if (key.name === "left") cursor = Math.max(0, cursor - 1);
885
- else if (key.name === "right") cursor = Math.min(chars.length, cursor + 1);
886
- else if (key.name === "home" || key.ctrl && key.name === "a") cursor = 0;
887
- else if (key.name === "end" || key.ctrl && key.name === "e") cursor = chars.length;
888
- else if (key.ctrl && key.name === "u") {
889
- editing = "";
890
- cursor = 0;
891
- } else if (key.name === "backspace" && cursor > 0) {
892
- chars.splice(--cursor, 1);
893
- editing = chars.join("");
894
- } else if (key.name === "delete") {
895
- chars.splice(cursor, 1);
896
- editing = chars.join("");
897
- } else if (text && !key.ctrl && !key.meta && chars.length < 8192) {
898
- const inserted = Array.from(terminalText(text)).slice(0, 8192 - chars.length);
899
- chars.splice(cursor, 0, ...inserted);
900
- editing = chars.join("");
901
- cursor += inserted.length;
902
- }
903
- } else {
904
- if (up) scroll = Math.max(0, scroll - 1);
905
- else if (down) scroll++;
906
- else if (key.name === "pageup") scroll = Math.max(0, scroll - Math.max(1, (output.rows || 24) - 8));
907
- else if (key.name === "pagedown") scroll += Math.max(1, (output.rows || 24) - 8);
908
- else if (key.name === "home") scroll = 0;
909
- else if (key.name === "end") scroll = Number.MAX_SAFE_INTEGER;
910
- else if (key.name === "d" || text === "d") {
911
- details = !details;
912
- scroll = 0;
913
- } else if (escape) {
914
- screen = screen === "result" ? "menu" : "form";
915
- scroll = 0;
916
- } else if (enter) {
917
- if (screen === "review") {
918
- void run().catch(crash);
919
- return;
920
- }
921
- if (command.operation.startsWith("connection.")) {
922
- void loadConnections().catch(crash);
923
- return;
924
- }
925
- screen = "form";
926
- scroll = 0;
927
- }
928
- }
929
- render();
930
- }
931
- const raw = input.isRaw ?? false;
932
- const flowing = input.readableFlowing === true;
933
- const onSignal = () => cancel(true);
934
- const onKeySafe = (text, key) => {
935
- try {
936
- onKey(text, key);
937
- } catch (error2) {
938
- crash(error2);
939
- }
940
- };
941
- const renderSafe = () => {
942
- try {
943
- render();
944
- } catch (error2) {
945
- crash(error2);
946
- }
947
- };
948
- const previousData = new Set(input.listeners("data"));
949
- const previousNewListener = new Set(input.listeners("newListener"));
950
- let timer;
951
- try {
952
- emitKeypressEvents(input);
953
- input.on("keypress", onKeySafe);
954
- input.on("end", onSignal);
955
- input.on("error", crash);
956
- output.on("error", crash);
957
- process.on("SIGINT", onSignal);
958
- process.on("SIGTERM", onSignal);
959
- output.on("resize", renderSafe);
960
- input.setRawMode(true);
961
- input.resume();
962
- output.write("\x1B[?1049h\x1B[?25l");
963
- timer = setInterval(() => {
964
- if (screen === "running") renderSafe();
965
- }, 250);
966
- render();
967
- void reloadCatalogue().catch(crash);
968
- void loadSqlclSettings().catch(crash);
969
- await done;
970
- } finally {
971
- if (timer) clearInterval(timer);
972
- input.off("keypress", onKeySafe);
973
- input.off("end", onSignal);
974
- input.off("error", crash);
975
- output.off("error", crash);
976
- process.off("SIGINT", onSignal);
977
- process.off("SIGTERM", onSignal);
978
- output.off("resize", renderSafe);
979
- for (const listener of input.listeners("data"))
980
- if (!previousData.has(listener)) input.off("data", listener);
981
- for (const listener of input.listeners("newListener"))
982
- if (!previousNewListener.has(listener))
983
- input.off("newListener", listener);
984
- input.setRawMode(raw);
985
- if (!flowing) input.pause();
986
- output.write("\x1B[0m\x1B[?25h\x1B[?1049l");
987
- }
988
- }
989
- export {
990
- runTui,
991
- terminalText
992
- };