@dokebi/cli 0.4.1 → 0.4.26

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 (188) hide show
  1. package/AI.md +262 -57
  2. package/README.md +35 -10
  3. package/catalog.json +2519 -257
  4. package/describe.schema.json +748 -0
  5. package/dist/api-app.d.ts +2 -0
  6. package/dist/api-app.js +35 -0
  7. package/dist/app-urls.js +3 -2
  8. package/dist/bin.js +527 -35
  9. package/dist/capability-guide.d.ts +30 -0
  10. package/dist/capability-guide.js +122 -0
  11. package/dist/catalog.d.ts +16 -2
  12. package/dist/catalog.js +19 -7
  13. package/dist/check/asset-rules.d.ts +6 -0
  14. package/dist/check/asset-rules.js +163 -0
  15. package/dist/check/browser-sources.d.ts +20 -0
  16. package/dist/check/browser-sources.js +89 -0
  17. package/dist/check/console-rules.d.ts +6 -0
  18. package/dist/check/console-rules.js +40 -0
  19. package/dist/check/error-rules.d.ts +21 -0
  20. package/dist/check/error-rules.js +212 -0
  21. package/dist/check/form-rules.d.ts +12 -0
  22. package/dist/check/form-rules.js +69 -6
  23. package/dist/check/formatter-rules.d.ts +2 -0
  24. package/dist/check/formatter-rules.js +71 -0
  25. package/dist/check/i18n-rules.d.ts +27 -0
  26. package/dist/check/i18n-rules.js +353 -0
  27. package/dist/check/index.d.ts +15 -1
  28. package/dist/check/index.js +89 -11
  29. package/dist/check/mail-rules.d.ts +4 -0
  30. package/dist/check/mail-rules.js +72 -0
  31. package/dist/check/module-rules.d.ts +13 -0
  32. package/dist/check/module-rules.js +104 -0
  33. package/dist/check/packages.d.ts +8 -0
  34. package/dist/check/packages.js +16 -0
  35. package/dist/check/pool-rules.d.ts +8 -0
  36. package/dist/check/pool-rules.js +164 -0
  37. package/dist/check/problems.d.ts +6 -1
  38. package/dist/check/problems.js +100 -2
  39. package/dist/check/project-rules.d.ts +23 -3
  40. package/dist/check/project-rules.js +265 -23
  41. package/dist/check/seal-rules.d.ts +6 -0
  42. package/dist/check/seal-rules.js +68 -0
  43. package/dist/check/security-rules.d.ts +10 -0
  44. package/dist/check/security-rules.js +450 -0
  45. package/dist/check/shared-state-rules.d.ts +2 -0
  46. package/dist/check/shared-state-rules.js +114 -0
  47. package/dist/check/source-rules.d.ts +26 -3
  48. package/dist/check/source-rules.js +249 -28
  49. package/dist/check/structure-rules.d.ts +23 -0
  50. package/dist/check/structure-rules.js +502 -0
  51. package/dist/check/timer-rules.d.ts +7 -0
  52. package/dist/check/timer-rules.js +543 -0
  53. package/dist/check/typescript.d.ts +35 -5
  54. package/dist/check/typescript.js +163 -17
  55. package/dist/check/vite-rules.d.ts +2 -0
  56. package/dist/check/vite-rules.js +136 -0
  57. package/dist/check/wait-rules.d.ts +16 -0
  58. package/dist/check/wait-rules.js +337 -0
  59. package/dist/commands.d.ts +7 -0
  60. package/dist/commands.js +307 -31
  61. package/dist/config-diff.d.ts +42 -0
  62. package/dist/config-diff.js +289 -0
  63. package/dist/config-edit.d.ts +35 -2
  64. package/dist/config-edit.js +145 -13
  65. package/dist/config-init.d.ts +15 -0
  66. package/dist/config-init.js +61 -0
  67. package/dist/config-paths.d.ts +22 -0
  68. package/dist/config-paths.js +82 -0
  69. package/dist/config-show.d.ts +82 -0
  70. package/dist/config-show.js +563 -0
  71. package/dist/config-values.d.ts +8 -0
  72. package/dist/config-values.js +68 -0
  73. package/dist/configuration-scaffold.d.ts +42 -0
  74. package/dist/configuration-scaffold.js +258 -4
  75. package/dist/configuration.d.ts +63 -3
  76. package/dist/configuration.js +178 -4
  77. package/dist/deployment.d.ts +2 -0
  78. package/dist/deployment.js +111 -0
  79. package/dist/describe-db.d.ts +22 -0
  80. package/dist/describe-db.js +176 -0
  81. package/dist/describe.d.ts +92 -0
  82. package/dist/describe.js +417 -0
  83. package/dist/dev-compose.d.ts +20 -0
  84. package/dist/dev-compose.js +154 -0
  85. package/dist/dev-guard.js +44 -0
  86. package/dist/dev-host.d.ts +13 -0
  87. package/dist/dev-host.js +55 -0
  88. package/dist/dev-infra.d.ts +72 -4
  89. package/dist/dev-infra.js +237 -17
  90. package/dist/dev-watch.d.ts +34 -0
  91. package/dist/dev-watch.js +192 -0
  92. package/dist/diagnostics.d.ts +10 -7
  93. package/dist/diagnostics.js +22 -4
  94. package/dist/drift.d.ts +50 -0
  95. package/dist/drift.js +218 -0
  96. package/dist/env-file.d.ts +25 -1
  97. package/dist/env-file.js +64 -6
  98. package/dist/errors.d.ts +7 -1
  99. package/dist/example-config.d.ts +2 -0
  100. package/dist/example-config.js +3 -0
  101. package/dist/first-admin.d.ts +43 -0
  102. package/dist/first-admin.js +175 -0
  103. package/dist/formatter-ignore.d.ts +37 -0
  104. package/dist/formatter-ignore.js +344 -0
  105. package/dist/generator.d.ts +45 -3
  106. package/dist/generator.js +205 -15
  107. package/dist/guide.d.ts +1 -1
  108. package/dist/guide.js +42 -21
  109. package/dist/install-module.d.ts +29 -3
  110. package/dist/install-module.js +142 -13
  111. package/dist/install-plan.js +5 -3
  112. package/dist/layout.d.ts +12 -1
  113. package/dist/layout.js +19 -6
  114. package/dist/log-writer-control.d.ts +2 -0
  115. package/dist/log-writer-control.js +106 -27
  116. package/dist/log-writer.js +43 -10
  117. package/dist/logging-config.d.ts +8 -3
  118. package/dist/logging-config.js +19 -5
  119. package/dist/manifest.d.ts +29 -1
  120. package/dist/manifest.js +84 -6
  121. package/dist/merge-install-defaults.d.ts +0 -1
  122. package/dist/merge-install-defaults.js +13 -47
  123. package/dist/npm-client.d.ts +59 -4
  124. package/dist/npm-client.js +220 -16
  125. package/dist/npm-paths.js +18 -4
  126. package/dist/openapi.d.ts +27 -0
  127. package/dist/openapi.js +649 -0
  128. package/dist/option-fixes.d.ts +39 -0
  129. package/dist/option-fixes.js +130 -0
  130. package/dist/output.d.ts +1 -1
  131. package/dist/output.js +5 -5
  132. package/dist/plugin-lifecycle.d.ts +34 -2
  133. package/dist/plugin-lifecycle.js +206 -20
  134. package/dist/plugin-options.d.ts +19 -4
  135. package/dist/plugin-options.js +138 -6
  136. package/dist/plugins-command.js +44 -7
  137. package/dist/process-group.d.ts +39 -0
  138. package/dist/process-group.js +132 -0
  139. package/dist/project-guide.d.ts +19 -1
  140. package/dist/project-guide.js +119 -20
  141. package/dist/project-inspector.d.ts +15 -0
  142. package/dist/project-inspector.js +481 -33
  143. package/dist/project-state.d.ts +5 -0
  144. package/dist/project-state.js +8 -0
  145. package/dist/project.d.ts +203 -19
  146. package/dist/project.js +3016 -336
  147. package/dist/recipe-facts.d.ts +127 -0
  148. package/dist/recipe-facts.js +563 -0
  149. package/dist/recipe-login.d.ts +63 -0
  150. package/dist/recipe-login.js +464 -0
  151. package/dist/recipe-mail.d.ts +21 -0
  152. package/dist/recipe-mail.js +214 -0
  153. package/dist/recipe-members.d.ts +35 -0
  154. package/dist/recipe-members.js +178 -0
  155. package/dist/recipe-record.d.ts +82 -7
  156. package/dist/recipe-record.js +226 -22
  157. package/dist/recipe-reset.d.ts +2 -0
  158. package/dist/recipe-reset.js +117 -0
  159. package/dist/recipe-site.d.ts +48 -0
  160. package/dist/recipe-site.js +396 -0
  161. package/dist/recipe-step.d.ts +42 -0
  162. package/dist/recipe-step.js +89 -0
  163. package/dist/recipe.d.ts +56 -20
  164. package/dist/recipe.js +703 -131
  165. package/dist/registry.d.ts +98 -2
  166. package/dist/registry.js +171 -5
  167. package/dist/reloadable.d.ts +35 -0
  168. package/dist/reloadable.js +257 -0
  169. package/dist/server.d.ts +50 -4
  170. package/dist/server.js +790 -103
  171. package/dist/settings.d.ts +48 -0
  172. package/dist/settings.js +168 -0
  173. package/dist/site-auth.d.ts +39 -0
  174. package/dist/site-auth.js +195 -0
  175. package/dist/template-fixes.d.ts +26 -0
  176. package/dist/template-fixes.js +285 -0
  177. package/dist/test-reporter.d.ts +49 -0
  178. package/dist/test-reporter.js +190 -0
  179. package/dist/transaction.d.ts +67 -0
  180. package/dist/transaction.js +428 -46
  181. package/dist/verify-tests.d.ts +98 -0
  182. package/dist/verify-tests.js +449 -0
  183. package/dist/verify.d.ts +113 -4
  184. package/dist/verify.js +326 -28
  185. package/dist/view-choice.d.ts +58 -0
  186. package/dist/view-choice.js +131 -0
  187. package/dist/vite-watch.js +4 -2
  188. package/package.json +8 -6
package/dist/commands.js CHANGED
@@ -5,12 +5,12 @@
5
5
  export const COMMANDS = [
6
6
  {
7
7
  name: 'guide',
8
- usage: 'dokebi guide [--json]',
8
+ usage: 'dokebi guide [--sync [--dry-run]] [--json]',
9
9
  summary: 'AI first page: how to turn a one-line request into an app (ask, install, verify) — read this first',
10
10
  },
11
11
  {
12
12
  name: 'init',
13
- usage: 'dokebi init <directory> [--core <package|local.tgz>] [--dry-run] [--json]',
13
+ usage: 'dokebi init <directory> [--core <package|local.tgz>] [--deploy] [--dry-run] [--json]',
14
14
  summary: 'Create a project (new folder, or an empty one such as .) with core, config, this CLI (@dokebi/cli) and TypeScript; prints next steps',
15
15
  },
16
16
  {
@@ -20,7 +20,7 @@ export const COMMANDS = [
20
20
  },
21
21
  {
22
22
  name: 'recipe',
23
- usage: 'dokebi recipe <name> [--dir <directory>] [--view <v>] [--db <d>] [--dev-infra <i>] [--admin <a>] [--port <n>] [--db-port <n>] [--answers-from <user|defaults>] [--defaults] [--dry-run] [--json]',
23
+ usage: 'dokebi recipe <name> [--dir <directory>] [--view <v>] [--db <d>] [--dev-infra <i>] [--admin <a>] [--mail <m>] [--apps <list>] [--domains <map>] [--realms <map>] [--worker <kind>] [--port <n>] [--db-port <n>] [--answers-from <user|defaults>] [--ai-chose <ids>] [--ai-reason <id=why>] [--defaults] [--dry-run] [--json]',
24
24
  summary: "Without every answer: the questions to ask the user, then end your turn (installs nothing); with the user's answers: plan, then apply with --answers-from user",
25
25
  },
26
26
  {
@@ -45,7 +45,7 @@ export const COMMANDS = [
45
45
  },
46
46
  {
47
47
  name: 'add',
48
- usage: 'dokebi add <package|local.tgz> [--set <path>=<value>]... [--adapter <pg|mysql2|mariadb|mongodb>] [--no-first-app] [--dry-run] [--json]',
48
+ usage: 'dokebi add <package|local.tgz> [--set <path>=<value>]... [--adapter <pg|mysql2|mariadb|mongodb>] [--startup <required|optional>] [--no-first-app] [--dry-run] [--json]',
49
49
  summary: 'Install and register a plugin (and the plugins it requires); no version picks the newest one for this core',
50
50
  },
51
51
  {
@@ -58,20 +58,40 @@ export const COMMANDS = [
58
58
  usage: 'dokebi remove <plugin-id> [--dry-run] [--json]',
59
59
  summary: 'Unregister and uninstall a plugin; user options are kept and marked',
60
60
  },
61
+ {
62
+ name: 'config show',
63
+ usage: 'dokebi config show [<plugin-id>[.<path>]] [--json]',
64
+ summary: 'Print the options of dokebi.yaml (with schema defaults) with every secret hidden, for output you share',
65
+ },
66
+ {
67
+ name: 'config diff',
68
+ usage: 'dokebi config diff [<plugin-id>] [--write [--values] [--prune] [--dry-run]] [--json]',
69
+ summary: 'Name the options dokebi.yaml and dokebi.example.yaml do not share, by path only (values are never shown); --write copies the ones only in dokebi.yaml into the sample without secrets; never dump or hand-edit the two files',
70
+ },
61
71
  {
62
72
  name: 'config set',
63
73
  usage: 'dokebi config set <plugin-id>.<path>=<value>... [--dry-run] [--json]',
64
- summary: 'Set non-secret options in dokebi.yaml (checked against the plugin schema)',
74
+ summary: 'Set non-secret options in dokebi.yaml (checked against the plugin schema); logging.<path> sets options.logging',
75
+ },
76
+ {
77
+ name: 'config unset',
78
+ usage: 'dokebi config unset <plugin-id>[.<path>]... [--dry-run] [--json]',
79
+ summary: 'Remove options from dokebi.yaml (and dokebi.example.yaml where it has them), e.g. a key the plugin no longer knows; never prints values',
80
+ },
81
+ {
82
+ name: 'config init',
83
+ usage: 'dokebi config init [--dry-run] [--json]',
84
+ summary: 'Write dokebi.yaml for a fresh copy of the project (a clone) from dokebi.example.yaml, with new generated secrets; never replaces an existing one',
65
85
  },
66
86
  {
67
87
  name: 'config secret',
68
- usage: 'dokebi config secret <plugin-id>.<path> [--dry-run] [--json]',
69
- summary: 'Write a generated secret (never shown) into dokebi.yaml, e.g. dokebi-session.secrets',
88
+ usage: 'dokebi config secret <plugin-id>.<path> [--stdin] [--rotate] [--dry-run] [--json]',
89
+ summary: 'Write a generated secret (never shown) into dokebi.yaml, e.g. dokebi-session.secrets, or with --stdin one you were given, e.g. logging.targets.studio.token; --rotate replaces one',
70
90
  },
71
91
  {
72
92
  name: 'dev-infra',
73
- usage: 'dokebi dev-infra [--db-port <n>] [--dry-run] [--json]',
74
- summary: 'Write compose.yaml, .env (generated password) and the dokebi.yaml address of the development database',
93
+ usage: 'dokebi dev-infra [up|start|stop|down|status] [--file <path>] [--db-port <n>] [--dry-run] [--json]',
94
+ summary: 'Prepare a database with YAML-only credentials, or manage development Compose services',
75
95
  },
76
96
  {
77
97
  name: 'migrate',
@@ -93,15 +113,30 @@ export const COMMANDS = [
93
113
  usage: 'dokebi check [--json]',
94
114
  summary: 'User-code conventions: empty catch, floating promises, syntax Node cannot strip, private or missing @dokebi imports, edited generated files, committable secrets',
95
115
  },
116
+ {
117
+ name: 'mail preview',
118
+ usage: 'dokebi mail preview <name> [--locale <locale>] [--json]',
119
+ summary: 'Render a mail template with preview.json in describe mode, without sending or connecting.',
120
+ },
121
+ {
122
+ name: 'describe',
123
+ usage: 'dokebi describe [--with-db] [--openapi [--app <name>]] [--timeout-ms <ms>] [--json]',
124
+ summary: 'What the app has, without serving: routes (auth, csrf), pages, jobs, schedules, error codes, option keys and env names (never values); --with-db adds the tables; --openapi: OpenAPI 3.1 per app',
125
+ },
126
+ {
127
+ name: 'drift',
128
+ usage: 'dokebi drift --expected <file> [--timeout-ms <ms>] [--json]',
129
+ summary: 'Compare the databases (connections, tables, collections, columns, column comments) with an expected file of the describe shape; it connects like describe --with-db; exit 1 on a difference',
130
+ },
96
131
  {
97
132
  name: 'verify',
98
- usage: 'dokebi verify [--wait-ms <ms>] [--json]',
99
- summary: 'doctor, check, typecheck, build, start, HTTP checks and stop; ok only when all pass (say done only then)',
133
+ usage: 'dokebi verify [--wait-ms <ms>] [--test-timeout-ms <ms>] [--json]',
134
+ summary: 'doctor, check, typecheck, build, test, start, HTTP checks and stop; ok only when all pass (say done only then)',
100
135
  },
101
136
  {
102
137
  name: 'app add',
103
- usage: 'dokebi app add <name> [--domain <host>]... [--template <name>] [--inertia [--ssr]] [--dry-run] [--json]',
104
- summary: 'Create the app src/apps/<name> (EJS, or Vue with --inertia) and map its domains',
138
+ usage: 'dokebi app add <name> [--domain <host>]... [--template <name>] [--inertia [--ssr]] [--view <ejs|none>] [--seal] [--answers-from <user|defaults>] [--ai-chose view] [--ai-reason view=<why>] [--dry-run] [--json]',
139
+ summary: 'Create the app src/apps/<name> (EJS, or Vue with --inertia: the default view) and map its domains; records who chose its view',
105
140
  },
106
141
  {
107
142
  name: 'app remove',
@@ -110,7 +145,7 @@ export const COMMANDS = [
110
145
  },
111
146
  {
112
147
  name: 'app link',
113
- usage: 'dokebi app link <name> <domain> [--template <name>] [--dry-run] [--json]',
148
+ usage: 'dokebi app link <name> <domain> [--template <name>] [--seal] [--dry-run] [--json]',
114
149
  summary: 'Serve an app on one more domain, optionally with another template',
115
150
  },
116
151
  {
@@ -130,10 +165,30 @@ export const COMMANDS = [
130
165
  summary: 'Unregister a feature module; its folder is kept',
131
166
  },
132
167
  { name: 'module list', usage: 'dokebi module list [--json]', summary: 'List feature modules' },
168
+ {
169
+ name: 'settings add',
170
+ usage: 'dokebi settings add <section> [--dry-run] [--json]',
171
+ summary: 'Declare the settings of the application in options.<section> (src/shared/settings/<section>.ts): checked at start, known to config and doctor, reloadable options apply live',
172
+ },
173
+ {
174
+ name: 'settings sync',
175
+ usage: 'dokebi settings sync [--dry-run] [--json]',
176
+ summary: 'Write the schema of every settings section again from its declaration and add new defaults to dokebi.yaml',
177
+ },
178
+ {
179
+ name: 'settings remove',
180
+ usage: 'dokebi settings remove <section> [--dry-run] [--json]',
181
+ summary: 'Unregister a settings section; its files and options are kept',
182
+ },
183
+ {
184
+ name: 'settings list',
185
+ usage: 'dokebi settings list [--json]',
186
+ summary: 'List the settings sections of the application',
187
+ },
133
188
  {
134
189
  name: 'dev',
135
190
  usage: 'dokebi dev',
136
- summary: 'Run the app in the foreground and restart it when src or dokebi.yaml change',
191
+ summary: 'Run the app in the foreground; restart it when src changes, reload dokebi.yaml in place when every changed option is reloadable',
137
192
  },
138
193
  {
139
194
  name: 'start',
@@ -145,6 +200,11 @@ export const COMMANDS = [
145
200
  usage: 'dokebi stop [--timeout-ms <ms>] [--force] [--json]',
146
201
  summary: 'Stop the background app (SIGTERM drain; SIGKILL with --force after the timeout)',
147
202
  },
203
+ {
204
+ name: 'reload',
205
+ usage: 'dokebi reload [--timeout-ms <ms>] [--json]',
206
+ summary: 'Make the background app read dokebi.yaml again (SIGHUP); options marked reloadable apply without a restart, others are refused with RELOAD_RESTART_REQUIRED',
207
+ },
148
208
  {
149
209
  name: 'status',
150
210
  usage: 'dokebi status [--json]',
@@ -163,14 +223,41 @@ export function commandHasFlag(command, flag) {
163
223
  return false;
164
224
  return new RegExp(`(^|[\\s\\[|])${flag.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?=[\\s\\]|]|$)`).test(usage);
165
225
  }
226
+ /**
227
+ * The commands whose usage line takes --dry-run, in table order (AI-M04 F8: `--help` said only
228
+ * "changing commands", and an agent tried `start --dry-run`). Derived, so it cannot drift.
229
+ */
230
+ export const DRY_RUN_COMMANDS = COMMANDS.filter((item) => commandHasFlag(item.name, '--dry-run')).map((item) => item.name);
231
+ /** DRY_RUN_COMMANDS on one line, subcommands grouped: `app add/remove/link/unlink`. */
232
+ export function dryRunList() {
233
+ const groups = new Map();
234
+ for (const name of DRY_RUN_COMMANDS) {
235
+ const [head = name, sub] = name.split(' ');
236
+ const subs = groups.get(head) ?? [];
237
+ if (sub)
238
+ subs.push(sub);
239
+ groups.set(head, subs);
240
+ }
241
+ return [...groups]
242
+ .map(([head, subs]) => (subs.length ? `${head} ${subs.join('/')}` : head))
243
+ .join(', ');
244
+ }
166
245
  /** `dokebi <command> --help`: what each flag does, and one example (D-0065, AI-M01). */
167
246
  export const COMMAND_DETAILS = {
168
247
  guide: {
169
- flags: [['--json', 'the guide as data: steps, rules, recipes, absent, commands']],
248
+ flags: [
249
+ ['--json', 'the guide as data: steps, rules, recipes, absent, commands'],
250
+ ['--sync', 'Refresh maintained AGENTS.md blocks, preserving project instructions'],
251
+ ['--dry-run', 'Preview --sync without writing files'],
252
+ ],
170
253
  example: 'npx dokebi guide --json',
171
254
  },
172
255
  init: {
173
256
  flags: [
257
+ [
258
+ '--deploy',
259
+ 'also create Dockerfile, .dockerignore, compose.deploy.yaml and DEPLOY.md; does not start or publish anything',
260
+ ],
174
261
  ['--core <package|local.tgz>', 'core to install (default: @dokebi/core in this CLI range)'],
175
262
  [
176
263
  '<directory>',
@@ -178,7 +265,7 @@ export const COMMAND_DETAILS = {
178
265
  ],
179
266
  ['--dry-run', 'check the folder and print the next steps; nothing is installed or written'],
180
267
  ],
181
- example: 'npx -y @dokebi/cli init my-blog && cd my-blog && npx dokebi guide --json\nnpx -y @dokebi/cli init . --json # in an empty work folder',
268
+ example: 'npx -y @dokebi/cli init my-app && cd my-app && npx dokebi guide --json\nnpx -y @dokebi/cli init . --json # in an empty work folder',
182
269
  },
183
270
  'recipe list': { flags: [], example: 'npx dokebi recipe list --json' },
184
271
  recipe: {
@@ -187,13 +274,33 @@ export const COMMAND_DETAILS = {
187
274
  '--dir <directory>',
188
275
  'project folder; a new folder is created with init first (default: current project)',
189
276
  ],
277
+ ['--apps <list>', 'site: comma-separated web,admin,api in that order, or none'],
278
+ [
279
+ '--domains <map>',
280
+ 'site: app=host pairs separated by commas, or auto for <app>.<project>.localhost',
281
+ ],
282
+ [
283
+ '--realms <map>',
284
+ 'site: app=realm pairs separated by commas, or auto for separate app realms',
285
+ ],
286
+ [
287
+ '--worker <kind>',
288
+ 'site: none | thread (CPU pool) | job (NATS queue; configure before starting)',
289
+ ],
190
290
  ['--view <v>', 'blog: ejs | inertia-vue | inertia-vue-ssr (inertia-react is absent)'],
191
291
  ['--db <d>', 'postgres | mysql | mariadb | mongodb | none (sqlite is absent)'],
192
292
  [
193
293
  '--dev-infra <i>',
194
294
  'docker (compose.yaml, generated password) | url (you write the URL); only with a database',
195
295
  ],
196
- ['--admin <a>', 'blog: none | app-code (no auth plugin: the app writes the login)'],
296
+ [
297
+ '--admin <a>',
298
+ 'blog: none | login (@dokebi/auth, needs a database) | app-code (an admin app whose login you write)',
299
+ ],
300
+ [
301
+ '--mail <m>',
302
+ 'none | mailpit (local Docker inbox and queue) | smtp | ses (existing configuration)',
303
+ ],
197
304
  ['--port <n>', 'web port (default 3000)'],
198
305
  [
199
306
  '--db-port <n>',
@@ -201,7 +308,15 @@ export const COMMAND_DETAILS = {
201
308
  ],
202
309
  [
203
310
  '--answers-from <user|defaults>',
204
- 'where the answers came from; applying needs it. user: the user answered these questions (ask, end your turn, wait). defaults: the user said to decide (same as --defaults). Recorded in `answersFrom` and `report`',
311
+ 'where the answers came from; applying needs it. user: the given flags are choices the user or the request explicitly made (ask, end your turn, wait), except those in --ai-chose. defaults: the user said to decide everything (same as --defaults). Recorded per answer (`answeredBy`) and in `report`',
312
+ ],
313
+ [
314
+ '--ai-chose <ids>',
315
+ 'comma-separated question ids of the given flags you chose yourself because the user left them to you (D-0095); recorded as `answeredBy: ai`. Never for a choice the user made',
316
+ ],
317
+ [
318
+ '--ai-reason <id=why>',
319
+ 'repeatable: one line (at most 200 characters) why you chose an --ai-chose answer; the user reads it in `report` and verify `tellUser`',
205
320
  ],
206
321
  [
207
322
  '--defaults',
@@ -209,7 +324,7 @@ export const COMMAND_DETAILS = {
209
324
  ],
210
325
  ['--dry-run', 'plan and preflight only; nothing is written'],
211
326
  ],
212
- example: 'npx dokebi recipe blog --json # the questions (exit 2, nothing installed): ask them and end your turn\nnpx dokebi recipe blog --view ejs --db postgres --dev-infra docker --admin none --answers-from user --dry-run --json',
327
+ example: 'npx dokebi recipe blog --json # the questions (exit 2, nothing installed): ask them and end your turn\nnpx dokebi recipe blog --view ejs --db postgres --dev-infra docker --admin none --mail none --answers-from user --dry-run --json',
213
328
  },
214
329
  'plugins list': {
215
330
  flags: [
@@ -223,7 +338,7 @@ export const COMMAND_DETAILS = {
223
338
  },
224
339
  'plugins search': {
225
340
  flags: [],
226
- example: 'npx dokebi plugins search login --json # status: absent (auth)',
341
+ example: 'npx dokebi plugins search upload --json # @dokebi/storage',
227
342
  },
228
343
  'plugins info': { flags: [], example: 'npx dokebi plugins info db --json' },
229
344
  'plugins doc': { flags: [], example: 'npx dokebi plugins doc dokebi-db --json' },
@@ -241,6 +356,10 @@ export const COMMAND_DETAILS = {
241
356
  '--adapter <id>',
242
357
  '@dokebi/db: pg | mysql2 | mariadb | mongodb (adapter file, connection main, driver, @types/pg)',
243
358
  ],
359
+ [
360
+ '--startup <required|optional>',
361
+ '@dokebi/db with --adapter: connections.main.startup (optional: the app starts and runs while that database is down; written only when given)',
362
+ ],
244
363
  [
245
364
  '--no-first-app',
246
365
  '@dokebi/app: do not create the EJS app main (add the first app with app add)',
@@ -260,29 +379,103 @@ export const COMMAND_DETAILS = {
260
379
  flags: [['--dry-run', 'plan only']],
261
380
  example: 'npx dokebi remove dokebi-health --dry-run --json',
262
381
  },
382
+ 'config show': {
383
+ flags: [
384
+ [
385
+ '<id>[.<path>]',
386
+ 'one section or value (default: every section); a value that is not set and has no default is UNKNOWN_OPTION',
387
+ ],
388
+ [
389
+ '--json',
390
+ '{ value, defaults, secrets }: secrets (schema writeOnly, keys named like secret/password/token/key, a URL password) show as <secret: set>; never a value',
391
+ ],
392
+ ],
393
+ example: 'npx dokebi config show dokebi-web --json\nnpx dokebi config show dokebi-web.port',
394
+ },
395
+ 'config diff': {
396
+ flags: [
397
+ ['<plugin-id>', 'one section (default: every section)'],
398
+ [
399
+ '--json',
400
+ '{ inSync, onlyInConfig, onlyInExample, different, secrets, exampleSecrets }: option paths only; secrets are never compared, and a secret with a value in the sample is listed in exampleSecrets',
401
+ ],
402
+ [
403
+ '--write',
404
+ 'copy the options only in dokebi.yaml into dokebi.example.yaml (comments kept): secrets inside them are left out, a list holding a secret is left out whole, values both files set stay as the sample has them; the result adds { written, diff } (diff: the comparison after the copy)',
405
+ ],
406
+ [
407
+ '--values',
408
+ 'with --write: also write the value dokebi.yaml has into dokebi.example.yaml for every path both set to different non-secret values (the paths of different); secrets are never compared, so never copied; the result adds { valuesWritten }',
409
+ ],
410
+ [
411
+ '--prune',
412
+ 'with --write: also remove from dokebi.example.yaml what only it has (an old section such as dokebi-logger, an option the plugin no longer has) and any secret with a value in it; dokebi.yaml is never changed; the result adds { pruned }',
413
+ ],
414
+ ['--dry-run', 'with --write: what would be copied or removed, nothing written'],
415
+ ],
416
+ example: 'npx dokebi config diff --json\nnpx dokebi config diff dokebi-web\nnpx dokebi config diff --write --json\nnpx dokebi config diff --write --values --json',
417
+ },
263
418
  'config set': {
264
419
  flags: [
265
- ['<id>.<path>=<value>', 'JSON when it parses, else text; checked against the plugin schema'],
420
+ [
421
+ '<id>.<path>=<value>',
422
+ 'JSON when it parses, else text; checked against the plugin schema. logging.<path> is options.logging (the @dokebi/logger options; without it level and plugins.<plugin> only); a list item is named by its name (else its type) or its index, as in logging.targets.file.enabled=true',
423
+ ],
266
424
  ['--dry-run', 'plan only'],
267
425
  ],
268
- example: 'npx dokebi config set dokebi-web.port=3000 --json',
426
+ example: 'npx dokebi config set dokebi-web.port=3000 --json\nnpx dokebi config set logging.level=debug logging.targets.file.enabled=true --json',
427
+ },
428
+ 'config unset': {
429
+ flags: [
430
+ [
431
+ '<id>.<path>',
432
+ 'removes it (and mappings left empty) from dokebi.yaml and dokebi.example.yaml; a path that is not set is OPTION_NOT_SET (a warning); a secret is removed with SECRET_REMOVED; values are never printed. logging.<path> is options.logging; list items by name (else type) or index. A section a file still holds as written is that section, never the plugin it names (the old options.dokebi-logger of a sample is removed as written, SECTION_NOT_READ, and options.logging stays)',
433
+ ],
434
+ [
435
+ '<id>',
436
+ 'a whole section, only one no installed plugin reads (an old section left in the files); a plugin section is INVALID_SET (remove the plugin instead)',
437
+ ],
438
+ ['--dry-run', 'plan only'],
439
+ ],
440
+ example: 'npx dokebi config unset dokebi-web.files --json\nnpx dokebi config unset dokebi-web.files --dry-run --json\nnpx dokebi config unset dokebi-logger --json',
441
+ },
442
+ 'config init': {
443
+ flags: [
444
+ [
445
+ '--dry-run',
446
+ 'plan only: the result names the secrets it would generate (never their values) and the placeholders left to fill',
447
+ ],
448
+ ],
449
+ example: 'npx dokebi config init --json\nnpx dokebi dev-infra --json\nnpx dokebi doctor --json',
269
450
  },
270
451
  'config secret': {
271
452
  flags: [
272
453
  [
273
454
  '<id>.<path>',
274
- 'writes a random secret there (a list when the schema wants one); never printed',
455
+ 'writes a random secret there (a list when the schema wants one); never printed; an existing value is kept (SECRET_KEPT), an empty one or a <placeholder> is filled; a URL takes only --stdin. logging.<path> is options.logging; a list item by its name (else type) or index',
456
+ ],
457
+ [
458
+ '--stdin',
459
+ 'write the secret read from stdin (one line, piped, or typed without echo on a terminal) instead of a generated one: a token someone issued, such as the token of a log receiver',
460
+ ],
461
+ [
462
+ '--rotate',
463
+ 'replace an existing value: a list gets the new secret first (it signs) and keeps the previous 2 after it (they still verify; older ones are dropped, SECRETS_DROPPED); a string is replaced. Restart the app afterwards',
275
464
  ],
276
465
  ['--dry-run', 'plan only'],
277
466
  ],
278
- example: 'npx dokebi config secret dokebi-session.secrets --json',
467
+ example: 'npx dokebi config secret dokebi-session.secrets --json\nnpx dokebi config secret dokebi-csrf.secrets --rotate --json\nnpx dokebi config secret logging.targets.studio.token --stdin --json < studio-token.txt',
279
468
  },
280
469
  'dev-infra': {
281
470
  flags: [
471
+ [
472
+ '--file <path>',
473
+ 'Compose file for an action; repeat for multiple files (default compose.yaml)',
474
+ ],
282
475
  ['--db-port <n>', 'host port of the database container (PORT_IN_USE names a free one)'],
283
476
  ['--dry-run', 'plan only'],
284
477
  ],
285
- example: 'npx dokebi dev-infra --db-port 5433 --json && docker compose up -d --wait',
478
+ example: 'npx dokebi dev-infra --db-port 5433 --json && npx dokebi dev-infra up --json',
286
479
  },
287
480
  migrate: { flags: [['--dry-run', 'plan only']], example: 'npx dokebi migrate --dry-run --json' },
288
481
  inspect: {
@@ -302,19 +495,79 @@ export const COMMAND_DETAILS = {
302
495
  ],
303
496
  example: 'npx dokebi check --json\n# a justified empty catch or background promise: // dokebi-check: allow empty-catch <reason>',
304
497
  },
498
+ 'mail preview': {
499
+ flags: [
500
+ ['--locale <locale>', 'locale to render (falls back to the configured default)'],
501
+ ['--json', 'subject, html, text and resolved locale in the standard envelope'],
502
+ ],
503
+ example: 'npx dokebi mail preview welcome --locale en --json',
504
+ },
505
+ describe: {
506
+ flags: [
507
+ [
508
+ '--with-db',
509
+ 'also read the tables of each dokebi-db connection with dokebi-migrate schema (this step connects); a connection it cannot read gets db.connections[].unavailable',
510
+ ],
511
+ [
512
+ '--timeout-ms <ms>',
513
+ 'how long loading and describing the app may take (default 30000); DESCRIBE_TIMEOUT after it',
514
+ ],
515
+ [
516
+ '--openapi',
517
+ 'one OpenAPI 3.1 document per app (operationId = route id); several apps: { apps: { <name>: document } }',
518
+ ],
519
+ ['--app <name>', 'with --openapi: only this app'],
520
+ [
521
+ '--json',
522
+ '{ schemaVersion, app, routes, pages, jobs, schedules, errors, config: { keys, env }, db, unavailable } (describe.schema.json); with --openapi the document(s)',
523
+ ],
524
+ ],
525
+ example: 'npx dokebi describe --json\nnpx dokebi describe --openapi --app main > openapi.json',
526
+ },
527
+ drift: {
528
+ flags: [
529
+ [
530
+ '--expected <file>',
531
+ 'JSON of the describe shape (only db.connections is read; a describe --json envelope works): connections, then tables or collections and columns where the file lists them, and a column comment where it gives a non-empty one (whitespace normalized)',
532
+ ],
533
+ [
534
+ '--timeout-ms <ms>',
535
+ 'how long loading the app and reading each database may take (default 30000)',
536
+ ],
537
+ [
538
+ '--json',
539
+ '{ ok, drift[{kind, connection, target, expected_value, code_value}], unavailable[{connection, reason}], compared, ms }; kind: connection | table | collection | column | column_comment; null means that side has none',
540
+ ],
541
+ ],
542
+ example: 'npx dokebi drift --expected design/describe.json --json',
543
+ },
305
544
  verify: {
306
- flags: [['--wait-ms <ms>', 'how long start waits for the started line (default 30000)']],
545
+ flags: [
546
+ ['--wait-ms <ms>', 'how long start waits for the started line (default 30000)'],
547
+ [
548
+ '--test-timeout-ms <ms>',
549
+ 'time limit of the test step, the package.json test script (default 300000)',
550
+ ],
551
+ ],
307
552
  example: 'npx dokebi verify --json',
308
553
  },
309
554
  'app add': {
310
555
  flags: [
311
- ['--domain <host>', 'host name served by the app (repeatable, no port)'],
556
+ [
557
+ '--domain <host>',
558
+ 'host name served by the app (repeatable, no port); auto: <app>.<project>.localhost',
559
+ ],
312
560
  ['--template <name>', 'views folder for those domains (default: default)'],
313
561
  ['--inertia', 'Vue pages with Vite (adds vite.config.ts, the build script, vite and vue)'],
314
562
  ['--ssr', 'with --inertia: server-rendered first HTML'],
563
+ [
564
+ '--view <ejs|none>',
565
+ 'EJS pages, or no views (a JSON API); unset: none where the recipe built no screens (api), else EJS',
566
+ ],
567
+ ['--seal', 'seal those domains (domains.<host>.seal: true; needs @dokebi/seal)'],
315
568
  ['--dry-run', 'plan only'],
316
569
  ],
317
- example: 'npx dokebi app add admin --domain admin.localhost --inertia --json',
570
+ example: 'npx dokebi app add admin --domain auto --inertia --json',
318
571
  },
319
572
  'app remove': {
320
573
  flags: [['--dry-run', 'plan only']],
@@ -323,6 +576,7 @@ export const COMMAND_DETAILS = {
323
576
  'app link': {
324
577
  flags: [
325
578
  ['--template <name>', 'views folder for this domain'],
579
+ ['--seal', 'seal this domain (domains.<host>.seal: true; needs @dokebi/seal)'],
326
580
  ['--dry-run', 'plan only'],
327
581
  ],
328
582
  example: 'npx dokebi app link shop sale.localhost --template sale --json',
@@ -337,13 +591,26 @@ export const COMMAND_DETAILS = {
337
591
  ['--no-views', 'routes answer JSON (no views folder)'],
338
592
  ['--dry-run', 'plan only'],
339
593
  ],
340
- example: 'npx dokebi module add posts --json',
594
+ example: 'npx dokebi module add items --json',
341
595
  },
342
596
  'module remove': {
343
597
  flags: [['--dry-run', 'plan only']],
344
- example: 'npx dokebi module remove posts --json',
598
+ example: 'npx dokebi module remove items --json',
345
599
  },
346
600
  'module list': { flags: [], example: 'npx dokebi module list --json' },
601
+ 'settings add': {
602
+ flags: [['--dry-run', 'plan only']],
603
+ example: 'npx dokebi settings add survey --json',
604
+ },
605
+ 'settings sync': {
606
+ flags: [['--dry-run', 'plan only']],
607
+ example: 'npx dokebi settings sync --json',
608
+ },
609
+ 'settings remove': {
610
+ flags: [['--dry-run', 'plan only']],
611
+ example: 'npx dokebi settings remove survey --json',
612
+ },
613
+ 'settings list': { flags: [], example: 'npx dokebi settings list --json' },
347
614
  dev: { flags: [], example: 'npx dokebi dev' },
348
615
  start: {
349
616
  flags: [
@@ -361,6 +628,15 @@ export const COMMAND_DETAILS = {
361
628
  ],
362
629
  example: 'npx dokebi stop --json',
363
630
  },
631
+ reload: {
632
+ flags: [
633
+ [
634
+ '--timeout-ms <ms>',
635
+ 'how long to wait for the outcome in the app log (default options.reload.prepareTimeoutMs + 5000)',
636
+ ],
637
+ ],
638
+ example: 'npx dokebi reload --json',
639
+ },
364
640
  status: { flags: [], example: 'npx dokebi status --json' },
365
641
  logs: {
366
642
  flags: [
@@ -0,0 +1,42 @@
1
+ import { type ConfigShowWarning } from './config-show.js';
2
+ export declare const EXAMPLE_FILE = "dokebi.example.yaml";
3
+ export interface ConfigDiffResult {
4
+ /** The settings file compared: dokebi.yaml, or the one DOKEBI_CONFIG names (M50). */
5
+ readonly file: string;
6
+ readonly example: typeof EXAMPLE_FILE;
7
+ /** The asked section, or null for every section. */
8
+ readonly section: string | null;
9
+ readonly inSync: boolean;
10
+ readonly onlyInConfig: string[];
11
+ readonly onlyInExample: string[];
12
+ readonly different: string[];
13
+ readonly secrets: string[];
14
+ readonly exampleSecrets: string[];
15
+ /**
16
+ * R4CA-2: what config show warns of either file, once per section (a schema that cannot be read:
17
+ * its text counts as secret in both files, so its sample values are listed in exampleSecrets;
18
+ * a section nothing declares). Absent when there is none.
19
+ */
20
+ readonly warnings?: ConfigShowWarning[];
21
+ }
22
+ /** A path only in dokebi.yaml and its value without secrets (`sampleAdditions`). */
23
+ export interface SampleAddition {
24
+ /** Section first: `['dokebi-web', 'port']`. */
25
+ readonly path: readonly string[];
26
+ readonly value: unknown;
27
+ }
28
+ export declare function configDiff(root: string, section?: string): Promise<ConfigDiffResult>;
29
+ /**
30
+ * What `config diff --write` copies into the sample: every path only in dokebi.yaml with the
31
+ * secrets inside it dropped (a secret leaf; a list with a secret item is left out whole).
32
+ */
33
+ export declare function sampleAdditions(root: string, configSource: string, exampleSource: string, section?: string,
34
+ /** The settings file `configSource` is (dokebi.yaml, or the one DOKEBI_CONFIG names). */
35
+ file?: string): Promise<{
36
+ result: ConfigDiffResult;
37
+ additions: SampleAddition[];
38
+ changes: SampleAddition[];
39
+ prunable: string[][];
40
+ }>;
41
+ /** The result for people: paths only. */
42
+ export declare function configDiffText(result: ConfigDiffResult): string;