@stacksjs/buddy 0.70.88 → 0.70.91

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 (143) hide show
  1. package/dist/cli.d.ts +1 -0
  2. package/dist/cli.js +165 -0
  3. package/dist/commands/about.d.ts +2 -0
  4. package/dist/commands/about.js +35 -0
  5. package/dist/commands/add.d.ts +2 -0
  6. package/dist/commands/add.js +33 -0
  7. package/dist/commands/auth.d.ts +2 -0
  8. package/dist/commands/auth.js +68 -0
  9. package/dist/commands/build.d.ts +2 -0
  10. package/dist/commands/build.js +153 -0
  11. package/dist/commands/cd.d.ts +20 -0
  12. package/dist/commands/cd.js +39 -0
  13. package/dist/commands/changelog.d.ts +2 -0
  14. package/dist/commands/changelog.js +32 -0
  15. package/dist/commands/clean.d.ts +2 -0
  16. package/dist/commands/clean.js +37 -0
  17. package/dist/commands/cloud.d.ts +2 -0
  18. package/dist/commands/cloud.js +503 -0
  19. package/dist/commands/commit.d.ts +2 -0
  20. package/dist/commands/commit.js +16 -0
  21. package/dist/commands/completion.d.ts +2 -0
  22. package/dist/commands/completion.js +143 -0
  23. package/dist/commands/config-migrate.d.ts +26 -0
  24. package/dist/commands/config-migrate.js +94 -0
  25. package/dist/commands/configure.d.ts +2 -0
  26. package/dist/commands/configure.js +48 -0
  27. package/dist/commands/create.d.ts +2 -0
  28. package/dist/commands/create.js +141 -0
  29. package/dist/commands/deploy.d.ts +107 -0
  30. package/dist/commands/deploy.js +1492 -0
  31. package/dist/commands/dev.d.ts +10 -0
  32. package/dist/commands/dev.js +915 -0
  33. package/dist/commands/dns.d.ts +2 -0
  34. package/dist/commands/dns.js +102 -0
  35. package/dist/commands/doctor.d.ts +2 -0
  36. package/dist/commands/doctor.js +285 -0
  37. package/dist/commands/domains.d.ts +2 -0
  38. package/dist/commands/domains.js +159 -0
  39. package/dist/commands/email.d.ts +2 -0
  40. package/dist/commands/email.js +524 -0
  41. package/dist/commands/env.d.ts +2 -0
  42. package/dist/commands/env.js +246 -0
  43. package/dist/commands/extension.d.ts +8 -0
  44. package/dist/commands/extension.js +78 -0
  45. package/dist/commands/features.d.ts +180 -0
  46. package/dist/commands/features.js +422 -0
  47. package/dist/commands/fresh.d.ts +2 -0
  48. package/dist/commands/fresh.js +39 -0
  49. package/dist/commands/generate.d.ts +2 -0
  50. package/dist/commands/generate.js +104 -0
  51. package/dist/commands/http.d.ts +2 -0
  52. package/dist/commands/http.js +33 -0
  53. package/dist/commands/index.d.ts +53 -0
  54. package/dist/commands/index.js +53 -0
  55. package/dist/commands/install.d.ts +2 -0
  56. package/dist/commands/install.js +22 -0
  57. package/dist/commands/key.d.ts +2 -0
  58. package/dist/commands/key.js +23 -0
  59. package/dist/commands/lint.d.ts +2 -0
  60. package/dist/commands/lint.js +46 -0
  61. package/dist/commands/list.d.ts +2 -0
  62. package/dist/commands/list.js +108 -0
  63. package/dist/commands/mail.d.ts +4 -0
  64. package/dist/commands/mail.js +752 -0
  65. package/dist/commands/maintenance.d.ts +2 -0
  66. package/dist/commands/maintenance.js +141 -0
  67. package/dist/commands/make.d.ts +2 -0
  68. package/dist/commands/make.js +375 -0
  69. package/dist/commands/migrate-project.d.ts +2 -0
  70. package/dist/commands/migrate-project.js +49 -0
  71. package/dist/commands/migrate.d.ts +4 -0
  72. package/dist/commands/migrate.js +455 -0
  73. package/dist/commands/outdated.d.ts +2 -0
  74. package/dist/commands/outdated.js +19 -0
  75. package/dist/commands/package.d.ts +2 -0
  76. package/dist/commands/package.js +17 -0
  77. package/dist/commands/phone.d.ts +2 -0
  78. package/dist/commands/phone.js +188 -0
  79. package/dist/commands/ports.d.ts +2 -0
  80. package/dist/commands/ports.js +118 -0
  81. package/dist/commands/prepublish.d.ts +2 -0
  82. package/dist/commands/prepublish.js +17 -0
  83. package/dist/commands/projects.d.ts +2 -0
  84. package/dist/commands/projects.js +29 -0
  85. package/dist/commands/publish.d.ts +2 -0
  86. package/dist/commands/publish.js +172 -0
  87. package/dist/commands/queue.d.ts +2 -0
  88. package/dist/commands/queue.js +248 -0
  89. package/dist/commands/release.d.ts +2 -0
  90. package/dist/commands/release.js +30 -0
  91. package/dist/commands/route.d.ts +2 -0
  92. package/dist/commands/route.js +21 -0
  93. package/dist/commands/saas.d.ts +2 -0
  94. package/dist/commands/saas.js +25 -0
  95. package/dist/commands/schedule.d.ts +2 -0
  96. package/dist/commands/schedule.js +61 -0
  97. package/dist/commands/search.d.ts +2 -0
  98. package/dist/commands/search.js +84 -0
  99. package/dist/commands/seed.d.ts +2 -0
  100. package/dist/commands/seed.js +71 -0
  101. package/dist/commands/serve.d.ts +25 -0
  102. package/dist/commands/serve.js +182 -0
  103. package/dist/commands/setup.d.ts +12 -0
  104. package/dist/commands/setup.js +215 -0
  105. package/dist/commands/share.d.ts +2 -0
  106. package/dist/commands/share.js +214 -0
  107. package/dist/commands/sms.d.ts +2 -0
  108. package/dist/commands/sms.js +328 -0
  109. package/dist/commands/stacks.d.ts +2 -0
  110. package/dist/commands/stacks.js +69 -0
  111. package/dist/commands/telemetry.d.ts +2 -0
  112. package/dist/commands/telemetry.js +74 -0
  113. package/dist/commands/test.d.ts +2 -0
  114. package/dist/commands/test.js +130 -0
  115. package/dist/commands/tinker.d.ts +2 -0
  116. package/dist/commands/tinker.js +37 -0
  117. package/dist/commands/types.d.ts +2 -0
  118. package/dist/commands/types.js +18 -0
  119. package/dist/commands/upgrade.d.ts +2 -0
  120. package/dist/commands/upgrade.js +98 -0
  121. package/dist/commands/version.d.ts +2 -0
  122. package/dist/commands/version.js +16 -0
  123. package/dist/config.d.ts +43 -0
  124. package/dist/config.js +223 -0
  125. package/dist/custom-cli.d.ts +1 -0
  126. package/dist/custom-cli.js +23 -0
  127. package/dist/index.d.ts +1 -0
  128. package/dist/index.js +1 -0
  129. package/dist/lazy-commands.d.ts +61 -0
  130. package/dist/lazy-commands.js +188 -0
  131. package/dist/migrators/index.d.ts +9 -0
  132. package/dist/migrators/index.js +62 -0
  133. package/dist/migrators/laravel/index.d.ts +2 -0
  134. package/dist/migrators/laravel/index.js +148 -0
  135. package/dist/migrators/laravel/migrations.d.ts +52 -0
  136. package/dist/migrators/laravel/migrations.js +232 -0
  137. package/dist/migrators/laravel/models.d.ts +34 -0
  138. package/dist/migrators/laravel/models.js +134 -0
  139. package/dist/migrators/rails/index.d.ts +2 -0
  140. package/dist/migrators/rails/index.js +11 -0
  141. package/dist/migrators/types.d.ts +44 -0
  142. package/dist/migrators/types.js +0 -0
  143. package/package.json +2 -2
@@ -0,0 +1,248 @@
1
+ import process from "node:process";
2
+ import { runAction } from "@stacksjs/actions";
3
+ import { intro, log, onUnknownSubcommand, outro } from "@stacksjs/cli";
4
+ import { Action } from "@stacksjs/enums";
5
+ import { ExitCode } from "@stacksjs/types";
6
+ export function queue(buddy) {
7
+ const descriptions = {
8
+ project: "Target a specific project",
9
+ queue: "Specify queue name",
10
+ verbose: "Enable verbose output",
11
+ id: "Job or batch ID",
12
+ all: "Apply to all items",
13
+ force: "Force the operation without confirmation",
14
+ connection: "Queue connection to use",
15
+ concurrency: "Number of concurrent workers"
16
+ };
17
+ buddy.command("queue:work", "Start processing jobs on the queue").option("-p, --project [project]", descriptions.project, { default: !1 }).option("-q, --queue [queue]", descriptions.queue, { default: !1 }).option("-c, --connection [connection]", descriptions.connection, { default: !1 }).option("--concurrency [concurrency]", descriptions.concurrency, { default: "1" }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
18
+ log.debug("Running `buddy queue:work` ...", options);
19
+ const PARENT_BACKSTOP_MS = (Number(process.env.STACKS_QUEUE_SHUTDOWN_GRACE_MS) || 1e4) + 5000;
20
+ let shuttingDown = !1;
21
+ const onSignal = (signal) => {
22
+ if (shuttingDown)
23
+ return;
24
+ shuttingDown = !0;
25
+ log.info(`[queue] Received ${signal}; waiting for the worker to drain\u2026`);
26
+ setTimeout(() => {
27
+ log.warn("[queue] Worker drain overran its window \u2014 forcing shutdown.");
28
+ process.exit(ExitCode.FatalError);
29
+ }, PARENT_BACKSTOP_MS).unref();
30
+ };
31
+ process.on("SIGINT", () => onSignal("SIGINT"));
32
+ process.on("SIGTERM", () => onSignal("SIGTERM"));
33
+ const perf = await intro("buddy queue:work"), result = await runAction(Action.QueueWork, options);
34
+ if (result.isErr) {
35
+ await outro("While running the queue:work command, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
36
+ process.exit(ExitCode.FatalError);
37
+ }
38
+ process.exit(ExitCode.Success);
39
+ });
40
+ buddy.command("queue:failed", "List all failed jobs").option("-p, --project [project]", descriptions.project, { default: !1 }).option("-q, --queue [queue]", descriptions.queue, { default: !1 }).option("--since [duration]", "Only show jobs failed since (e.g. 1h, 30m, 2d)", { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
41
+ log.debug("Running `buddy queue:failed` ...", options);
42
+ const perf = await intro("buddy queue:failed"), result = await runAction(Action.QueueFailed, options);
43
+ if (result.isErr) {
44
+ await outro("While running the queue:failed command, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
45
+ process.exit(ExitCode.FatalError);
46
+ }
47
+ await outro("Listed all failed jobs", {
48
+ startTime: perf,
49
+ useSeconds: !0
50
+ });
51
+ process.exit(ExitCode.Success);
52
+ });
53
+ buddy.command("queue:retry [id]", "Retry failed jobs").option("-p, --project [project]", descriptions.project, { default: !1 }).option("-q, --queue [queue]", descriptions.queue, { default: !1 }).option("--all", descriptions.all, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (id, options) => {
54
+ log.debug("Running `buddy queue:retry` ...", options);
55
+ const perf = await intro("buddy queue:retry"), result = await runAction(Action.QueueRetry, { ...options, id });
56
+ if (result.isErr) {
57
+ await outro("While running the queue:retry command, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
58
+ process.exit(ExitCode.FatalError);
59
+ }
60
+ const message = options.all ? "Retried all failed jobs" : `Retried failed job ${id}`;
61
+ await outro(message, {
62
+ startTime: perf,
63
+ useSeconds: !0
64
+ });
65
+ process.exit(ExitCode.Success);
66
+ });
67
+ buddy.command("queue:clear", "Clear all jobs from the queue").option("-p, --project [project]", descriptions.project, { default: !1 }).option("-q, --queue [queue]", descriptions.queue, { default: !1 }).option("--force", descriptions.force, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
68
+ log.debug("Running `buddy queue:clear` ...", options);
69
+ const perf = await intro("buddy queue:clear"), result = await runAction(Action.QueueClear, options);
70
+ if (result.isErr) {
71
+ await outro("While running the queue:clear command, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
72
+ process.exit(ExitCode.FatalError);
73
+ }
74
+ const queueName = options.queue || "default";
75
+ await outro(`Cleared all jobs from the "${queueName}" queue`, {
76
+ startTime: perf,
77
+ useSeconds: !0
78
+ });
79
+ process.exit(ExitCode.Success);
80
+ });
81
+ buddy.command("queue:list", "List queued jobs (flat row view, filterable by queue/status)").option("-p, --project [project]", descriptions.project, { default: !1 }).option("-q, --queue [queue]", descriptions.queue, { default: !1 }).option("--status [status]", "Filter by pending | reserved | delayed", { default: !1 }).option("--limit [limit]", "Maximum rows to display (default 50)", { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
82
+ log.debug("Running `buddy queue:list` ...", options);
83
+ const perf = await intro("buddy queue:list"), result = await runAction(Action.QueueList, options);
84
+ if (result.isErr) {
85
+ await outro("While running the queue:list command, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
86
+ process.exit(ExitCode.FatalError);
87
+ }
88
+ await outro("Listed queued jobs", {
89
+ startTime: perf,
90
+ useSeconds: !0
91
+ });
92
+ process.exit(ExitCode.Success);
93
+ });
94
+ buddy.command("queue:status", "Display the status of queue workers").option("-p, --project [project]", descriptions.project, { default: !1 }).option("-q, --queue [queue]", descriptions.queue, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
95
+ log.debug("Running `buddy queue:status` ...", options);
96
+ const perf = await intro("buddy queue:status"), result = await runAction(Action.QueueStatus, options);
97
+ if (result.isErr) {
98
+ await outro("While running the queue:status command, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
99
+ process.exit(ExitCode.FatalError);
100
+ }
101
+ await outro("Queue status displayed", {
102
+ startTime: perf,
103
+ useSeconds: !0
104
+ });
105
+ process.exit(ExitCode.Success);
106
+ });
107
+ buddy.command("queue:flush", "Delete all failed jobs").option("-p, --project [project]", descriptions.project, { default: !1 }).option("-q, --queue [queue]", descriptions.queue, { default: !1 }).option("--force", descriptions.force, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
108
+ log.debug("Running `buddy queue:flush` ...", options);
109
+ const perf = await intro("buddy queue:flush"), result = await runAction(Action.QueueFlush, options);
110
+ if (result.isErr) {
111
+ await outro("While running the queue:flush command, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
112
+ process.exit(ExitCode.FatalError);
113
+ }
114
+ await outro("Flushed all failed jobs", {
115
+ startTime: perf,
116
+ useSeconds: !0
117
+ });
118
+ process.exit(ExitCode.Success);
119
+ });
120
+ buddy.command("queue:dlq", "List dead-letter jobs (jobs that re-failed after retry)").option("-p, --project [project]", descriptions.project, { default: !1 }).option("-q, --queue [queue]", descriptions.queue, { default: !1 }).option("--reason [reason]", "Filter by reason (repeat-failure | poison-detected | circuit-broken | manual)", { default: !1 }).option("--since [duration]", "Only show rows dead-lettered since (e.g. 1h, 30m, 2d)", { default: !1 }).option("--limit [limit]", "Maximum rows to display (default 100)", { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
121
+ log.debug("Running `buddy queue:dlq` ...", options);
122
+ const perf = await intro("buddy queue:dlq"), result = await runAction(Action.QueueDlq, options);
123
+ if (result.isErr) {
124
+ await outro("While running queue:dlq there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
125
+ process.exit(ExitCode.FatalError);
126
+ }
127
+ await outro("Listed dead-letter jobs", { startTime: perf, useSeconds: !0 });
128
+ process.exit(ExitCode.Success);
129
+ });
130
+ buddy.command("queue:dlq:retry", "Re-enqueue a dead-letter job back into its queue").option("--id [id]", "Dead-letter row id to retry", { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
131
+ log.debug("Running `buddy queue:dlq:retry` ...", options);
132
+ const perf = await intro("buddy queue:dlq:retry"), result = await runAction(Action.QueueDlqRetry, options);
133
+ if (result.isErr) {
134
+ await outro("While running queue:dlq:retry there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
135
+ process.exit(ExitCode.FatalError);
136
+ }
137
+ await outro("Re-enqueued dead-letter job", { startTime: perf, useSeconds: !0 });
138
+ process.exit(ExitCode.Success);
139
+ });
140
+ buddy.command("queue:dlq:purge", "Delete dead-letter rows older than a retention window").option("--older-than-days [days]", "Retention window in days (default 30)", { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
141
+ log.debug("Running `buddy queue:dlq:purge` ...", options);
142
+ const perf = await intro("buddy queue:dlq:purge"), result = await runAction(Action.QueueDlqPurge, options);
143
+ if (result.isErr) {
144
+ await outro("While running queue:dlq:purge there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
145
+ process.exit(ExitCode.FatalError);
146
+ }
147
+ await outro("Purged dead-letter rows", { startTime: perf, useSeconds: !0 });
148
+ process.exit(ExitCode.Success);
149
+ });
150
+ buddy.command("queue:quarantine", "List quarantined jobs (or --add a manual quarantine)").option("--add [jobName]", "Manually quarantine a job class", { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
151
+ log.debug("Running `buddy queue:quarantine` ...", options);
152
+ const perf = await intro("buddy queue:quarantine"), result = await runAction(Action.QueueQuarantine, options);
153
+ if (result.isErr) {
154
+ await outro("While running queue:quarantine there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
155
+ process.exit(ExitCode.FatalError);
156
+ }
157
+ await outro("queue:quarantine done", { startTime: perf, useSeconds: !0 });
158
+ process.exit(ExitCode.Success);
159
+ });
160
+ buddy.command("queue:unquarantine", "Lift the quarantine on a job class").option("--name [name]", "Job class name to unquarantine", { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
161
+ log.debug("Running `buddy queue:unquarantine` ...", options);
162
+ const perf = await intro("buddy queue:unquarantine"), result = await runAction(Action.QueueUnquarantine, options);
163
+ if (result.isErr) {
164
+ await outro("While running queue:unquarantine there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
165
+ process.exit(ExitCode.FatalError);
166
+ }
167
+ await outro("Unquarantined", { startTime: perf, useSeconds: !0 });
168
+ process.exit(ExitCode.Success);
169
+ });
170
+ buddy.command("queue:pause", "Manually pause a queue (circuit-breaker)").option("--queue [name]", "Queue name to pause", { default: !1 }).option("--for [seconds]", "Pause duration in seconds (default 300)", { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
171
+ log.debug("Running `buddy queue:pause` ...", options);
172
+ const perf = await intro("buddy queue:pause"), result = await runAction(Action.QueuePause, options);
173
+ if (result.isErr) {
174
+ await outro("While running queue:pause there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
175
+ process.exit(ExitCode.FatalError);
176
+ }
177
+ await outro("Paused queue", { startTime: perf, useSeconds: !0 });
178
+ process.exit(ExitCode.Success);
179
+ });
180
+ buddy.command("queue:resume", "Resume a paused queue (circuit-breaker)").option("--queue [name]", "Queue name to resume", { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
181
+ log.debug("Running `buddy queue:resume` ...", options);
182
+ const perf = await intro("buddy queue:resume"), result = await runAction(Action.QueueResume, options);
183
+ if (result.isErr) {
184
+ await outro("While running queue:resume there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
185
+ process.exit(ExitCode.FatalError);
186
+ }
187
+ await outro("Resumed queue", { startTime: perf, useSeconds: !0 });
188
+ process.exit(ExitCode.Success);
189
+ });
190
+ buddy.command("queue:table", "Create a migration for the jobs database table").option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
191
+ log.debug("Running `buddy queue:table` ...", options);
192
+ const perf = await intro("buddy queue:table"), result = await runAction(Action.QueueTable, options);
193
+ if (result.isErr) {
194
+ await outro("While running the queue:table command, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
195
+ process.exit(ExitCode.FatalError);
196
+ }
197
+ await outro("Jobs table migration created", {
198
+ startTime: perf,
199
+ useSeconds: !0
200
+ });
201
+ process.exit(ExitCode.Success);
202
+ });
203
+ buddy.command("queue:monitor", "Monitor queue status in real-time").option("-p, --project [project]", descriptions.project, { default: !1 }).option("-q, --queue [queue]", descriptions.queue, { default: !1 }).option("-i, --interval [interval]", "Refresh interval in milliseconds", { default: "2000" }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
204
+ log.debug("Running `buddy queue:monitor` ...", options);
205
+ const result = await runAction(Action.QueueMonitor, options);
206
+ if (result.isErr) {
207
+ log.error("While running the queue:monitor command, there was an issue", result.error);
208
+ process.exit(ExitCode.FatalError);
209
+ }
210
+ process.exit(ExitCode.Success);
211
+ });
212
+ buddy.command("queue:inspect [id]", "Inspect a specific job").option("-p, --project [project]", descriptions.project, { default: !1 }).option("--failed", "Inspect a failed job", { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (id, options) => {
213
+ log.debug("Running `buddy queue:inspect` ...", options);
214
+ const perf = await intro("buddy queue:inspect"), result = await runAction(Action.QueueInspect, { ...options, id });
215
+ if (result.isErr) {
216
+ await outro("While running the queue:inspect command, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
217
+ process.exit(ExitCode.FatalError);
218
+ }
219
+ await outro("Job inspection complete", {
220
+ startTime: perf,
221
+ useSeconds: !0
222
+ });
223
+ process.exit(ExitCode.Success);
224
+ });
225
+ buddy.command("queue:schedule", "Start the job scheduler for cron-based jobs").option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
226
+ log.debug("Running `buddy queue:schedule` ...", options);
227
+ const result = await runAction(Action.QueueSchedule, options);
228
+ if (result.isErr) {
229
+ log.error("While running the queue:schedule command, there was an issue", result.error);
230
+ process.exit(ExitCode.FatalError);
231
+ }
232
+ process.exit(ExitCode.Success);
233
+ });
234
+ buddy.command("queue:schedule:list", "List all scheduled jobs").option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
235
+ log.debug("Running `buddy queue:schedule:list` ...", options);
236
+ const perf = await intro("buddy queue:schedule:list"), result = await runAction(Action.QueueScheduleList, options);
237
+ if (result.isErr) {
238
+ await outro("While running the queue:schedule:list command, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
239
+ process.exit(ExitCode.FatalError);
240
+ }
241
+ await outro("Listed all scheduled jobs", {
242
+ startTime: perf,
243
+ useSeconds: !0
244
+ });
245
+ process.exit(ExitCode.Success);
246
+ });
247
+ onUnknownSubcommand(buddy, "queue");
248
+ }
@@ -0,0 +1,2 @@
1
+ import type { CLI } from '@stacksjs/types';
2
+ export declare function release(buddy: CLI): void;
@@ -0,0 +1,30 @@
1
+ import process from "node:process";
2
+ import { runAction } from "@stacksjs/actions";
3
+ import { intro, italic, log, onUnknownSubcommand, outro } from "@stacksjs/cli";
4
+ import { Action } from "@stacksjs/enums";
5
+ import { ExitCode } from "@stacksjs/types";
6
+ const descriptions = {
7
+ release: "Release a new version of your libraries/packages",
8
+ project: "Target a specific project",
9
+ dryRun: "Run the release without actually releasing",
10
+ bump: "Non-interactive bump: patch | minor | major | prepatch | preminor | premajor | prerelease | x.y.z",
11
+ verbose: "Enable verbose output"
12
+ };
13
+ export function release(buddy) {
14
+ buddy.command("release", descriptions.release).option("--dry-run", descriptions.dryRun, { default: !1 }).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--bump <type>", descriptions.bump).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
15
+ log.debug("Running `buddy release` ...", options);
16
+ if (options.dryRun)
17
+ log.warn("Dry run enabled. No changes will be made or committed.");
18
+ const startTime = await intro("buddy release"), result = await runAction(Action.Release, options);
19
+ if (result.isErr) {
20
+ log.error("Failed to release", result.error);
21
+ process.exit(ExitCode.FatalError);
22
+ }
23
+ await outro("Triggered CI/CD Release via GitHub Actions", {
24
+ startTime,
25
+ useSeconds: !0
26
+ });
27
+ log.info(`Follow along: ${italic("https://github.com/stacksjs/stacks/actions")}`);
28
+ });
29
+ onUnknownSubcommand(buddy, "release");
30
+ }
@@ -0,0 +1,2 @@
1
+ import type { CLI } from '@stacksjs/types';
2
+ export declare function route(buddy: CLI): void;
@@ -0,0 +1,21 @@
1
+ import process from "node:process";
2
+ import { runAction } from "@stacksjs/actions";
3
+ import { intro, onUnknownSubcommand, outro } from "@stacksjs/cli";
4
+ import { Action } from "@stacksjs/enums";
5
+ import { ExitCode } from "@stacksjs/types";
6
+ export function route(buddy) {
7
+ const descriptions = {
8
+ route: "Lists your routes",
9
+ verbose: "Enable verbose output"
10
+ };
11
+ buddy.command("route:list", descriptions.route).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
12
+ const perf = await intro("buddy route:list"), result = await runAction(Action.RouteList, options);
13
+ if (result.isErr) {
14
+ await outro("While running the route:list command, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
15
+ process.exit(ExitCode.FatalError);
16
+ }
17
+ await outro("Successfully listed routes");
18
+ process.exit(ExitCode.Success);
19
+ });
20
+ onUnknownSubcommand(buddy, "route");
21
+ }
@@ -0,0 +1,2 @@
1
+ import type { CLI } from '@stacksjs/types';
2
+ export declare function saas(buddy: CLI): void;
@@ -0,0 +1,25 @@
1
+ import process from "node:process";
2
+ import { runAction } from "@stacksjs/actions";
3
+ import { intro, log, outro } from "@stacksjs/cli";
4
+ import { Action } from "@stacksjs/enums";
5
+ import { ExitCode } from "@stacksjs/types";
6
+ export function saas(buddy) {
7
+ const descriptions = {
8
+ stripe: "Sets up stripe products in the dashboard",
9
+ project: "Target a specific project",
10
+ verbose: "Enable verbose output"
11
+ };
12
+ buddy.command("stripe:setup", descriptions.stripe).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
13
+ log.debug("Running `buddy stripe:setup` ...", options);
14
+ const perf = await intro("buddy stripe:setup"), result = await runAction(Action.StripeSetup, options);
15
+ if (result.isErr) {
16
+ await outro("While running the stripe:setup command, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
17
+ process.exit(ExitCode.FatalError);
18
+ }
19
+ await outro("Stripe products created successfully", {
20
+ startTime: perf,
21
+ useSeconds: !0
22
+ });
23
+ process.exit(ExitCode.Success);
24
+ });
25
+ }
@@ -0,0 +1,2 @@
1
+ import type { CLI } from '@stacksjs/types';
2
+ export declare function schedule(buddy: CLI): void;
@@ -0,0 +1,61 @@
1
+ import process from "node:process";
2
+ import { runAction } from "@stacksjs/actions";
3
+ import { intro, log, outro } from "@stacksjs/cli";
4
+ import { Action } from "@stacksjs/enums";
5
+ import { ExitCode } from "@stacksjs/types";
6
+ export function schedule(buddy) {
7
+ const descriptions = {
8
+ project: "Target a specific project",
9
+ schedule: "Run the scheduler",
10
+ verbose: "Enable verbose output",
11
+ list: "List all registered scheduled tasks with their next run time",
12
+ status: "Show currently-held overlap locks (this-process only)"
13
+ };
14
+ buddy.command("schedule:run", descriptions.schedule).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
15
+ log.debug("Running `buddy schedule:run` ...", options);
16
+ const perf = await intro("buddy schedule:run"), result = await runAction(Action.ScheduleRun, options);
17
+ if (result.isErr) {
18
+ await outro("While running the schedule:run command, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
19
+ process.exit(ExitCode.FatalError);
20
+ }
21
+ process.exit(ExitCode.Success);
22
+ });
23
+ buddy.command("schedule:list", descriptions.list).action(async () => {
24
+ try {
25
+ await import(`${process.cwd()}/routes/scheduler.ts`).catch(() => {
26
+ log.warn("[schedule:list] no routes/scheduler.ts found \u2014 registry will be empty unless tasks were registered elsewhere");
27
+ });
28
+ const { Schedule } = await import("@stacksjs/scheduler"), jobs = Schedule.listJobs();
29
+ if (jobs.length === 0) {
30
+ log.info("No scheduled tasks registered.");
31
+ process.exit(ExitCode.Success);
32
+ }
33
+ const nameWidth = Math.max(4, ...jobs.map((j) => j.name.length)), patternWidth = Math.max(7, ...jobs.map((j) => (j.pattern ?? "").length));
34
+ log.info(`${"NAME".padEnd(nameWidth)} ${"PATTERN".padEnd(patternWidth)} TIMEZONE NEXT RUN (UTC)`);
35
+ for (const j of jobs) {
36
+ const next = j.nextRun ? j.nextRun.toISOString() : "\u2014";
37
+ log.info(`${j.name.padEnd(nameWidth)} ${(j.pattern ?? "").padEnd(patternWidth)} ${(j.timezone ?? "UTC").padEnd(25)} ${next}`);
38
+ }
39
+ process.exit(ExitCode.Success);
40
+ } catch (err) {
41
+ log.error(`[schedule:list] failed: ${err instanceof Error ? err.message : String(err)}`);
42
+ process.exit(ExitCode.FatalError);
43
+ }
44
+ });
45
+ buddy.command("schedule:status", descriptions.status).action(async () => {
46
+ try {
47
+ const { Schedule } = await import("@stacksjs/scheduler"), held = Schedule.listLocks();
48
+ if (held.length === 0)
49
+ log.info("No in-process scheduler locks held.");
50
+ else {
51
+ log.info(`Held locks (${held.length}):`);
52
+ for (const name of held)
53
+ log.info(` \u2022 ${name}`);
54
+ }
55
+ process.exit(ExitCode.Success);
56
+ } catch (err) {
57
+ log.error(`[schedule:status] failed: ${err instanceof Error ? err.message : String(err)}`);
58
+ process.exit(ExitCode.FatalError);
59
+ }
60
+ });
61
+ }
@@ -0,0 +1,2 @@
1
+ import type { CLI } from '@stacksjs/types';
2
+ export declare function search(buddy: CLI): void;
@@ -0,0 +1,84 @@
1
+ import process from "node:process";
2
+ import { runAction } from "@stacksjs/actions";
3
+ import { intro, log, outro } from "@stacksjs/cli";
4
+ import { Action } from "@stacksjs/enums";
5
+ import { ExitCode } from "@stacksjs/types";
6
+ export function search(buddy) {
7
+ const descriptions = {
8
+ import: "Indexes database data to search engine",
9
+ flush: "Flushes all data from search engine",
10
+ settings: "Update index settings",
11
+ list: "List index settings",
12
+ model: "Target a specific model",
13
+ project: "Target a specific project",
14
+ verbose: "Enable verbose output"
15
+ };
16
+ buddy.command("search-engine:update", descriptions.import).option("-m, --model [model]", descriptions.model, { default: "" }).option("-f, --flush [flush]", descriptions.flush, { default: !1 }).option("-s, --settings [settings]", descriptions.settings, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).option("-p, --project [project]", descriptions.project, { default: !1 }).action(async (options) => {
17
+ log.debug("Running `search-engine:update` ...", options);
18
+ let actionString = Action.SearchEngineImport, introString = "search-engine:update";
19
+ if (options.model) {
20
+ actionString = Action.SearchEngineImport;
21
+ introString = `search-engine:update --model ${options.model}`;
22
+ }
23
+ if (options.settings) {
24
+ actionString = Action.SearchEnginePushSettings;
25
+ introString = "search-engine:update --settings";
26
+ }
27
+ if (options.flush) {
28
+ actionString = Action.SearchEngineFlush;
29
+ introString = "search-engine:update --flush";
30
+ }
31
+ const perf = await intro(introString), result = await runAction(actionString, options);
32
+ if (result.isErr) {
33
+ await outro("While running the search-engine:update command, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
34
+ process.exit(ExitCode.FatalError);
35
+ }
36
+ await outro("Successfully imported model data to search engine.", {
37
+ startTime: perf,
38
+ useSeconds: !0
39
+ });
40
+ });
41
+ buddy.command("search-engine:settings", descriptions.list).option("-m, --model [model]", descriptions.model, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
42
+ if (!options.model)
43
+ log.error("Missing required option --model");
44
+ log.debug("Running `search-engine:settings` ...", options);
45
+ const perf = await intro("search-engine:settings"), result = await runAction(Action.SearchEngineListSettings, options);
46
+ if (result.isErr) {
47
+ await outro("While running the search-engine:settings command, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
48
+ process.exit(ExitCode.FatalError);
49
+ }
50
+ await outro("Successfully listed search engine index settings.", {
51
+ startTime: perf,
52
+ useSeconds: !0
53
+ });
54
+ process.exit(ExitCode.Success);
55
+ });
56
+ buddy.command("search:reindex [model]", "Re-index a model (Scout-style alias for search-engine:update)").option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (model, options) => {
57
+ const opts = { ...options, model: model ?? "" };
58
+ log.debug("Running `search:reindex` ...", opts);
59
+ const perf = await intro(`search:reindex${model ? ` ${model}` : ""}`), result = await runAction(Action.SearchEngineImport, opts);
60
+ if (result.isErr) {
61
+ await outro("While running the search:reindex command, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
62
+ process.exit(ExitCode.FatalError);
63
+ }
64
+ await outro(model ? `Reindexed ${model}.` : "Reindexed all searchable models.", {
65
+ startTime: perf,
66
+ useSeconds: !0
67
+ });
68
+ process.exit(ExitCode.Success);
69
+ });
70
+ buddy.command("search:flush [model]", "Delete all documents from a model index (Scout-style alias)").option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (model, options) => {
71
+ const opts = { ...options, model: model ?? "", flush: !0 };
72
+ log.debug("Running `search:flush` ...", opts);
73
+ const perf = await intro(`search:flush${model ? ` ${model}` : ""}`), result = await runAction(Action.SearchEngineFlush, opts);
74
+ if (result.isErr) {
75
+ await outro("While running the search:flush command, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
76
+ process.exit(ExitCode.FatalError);
77
+ }
78
+ await outro(model ? `Flushed ${model} from search.` : "Flushed all model indices.", {
79
+ startTime: perf,
80
+ useSeconds: !0
81
+ });
82
+ process.exit(ExitCode.Success);
83
+ });
84
+ }
@@ -0,0 +1,2 @@
1
+ import type { CLI } from '@stacksjs/types';
2
+ export declare function seed(buddy: CLI): void;
@@ -0,0 +1,71 @@
1
+ import process from "node:process";
2
+ import { intro, log, onUnknownSubcommand, outro } from "@stacksjs/cli";
3
+ import { ExitCode } from "@stacksjs/types";
4
+ export function seed(buddy) {
5
+ const descriptions = {
6
+ seed: "Seed your database",
7
+ project: "Target a specific project",
8
+ verbose: "Enable verbose output"
9
+ };
10
+ buddy.command("seed", descriptions.seed).alias("db:seed").option("-p, --project [project]", descriptions.project, { default: !1 }).option("-c, --class [class]", "Run a specific seeder class from database/seeders/", { default: "" }).option("--allow-protected", "Seed auth/oauth models even on a non-fresh DB (will invalidate live tokens)", { default: !1 }).option("--fresh", "Truncate tables before seeding", { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
11
+ log.debug("Running `buddy seed` ...", options);
12
+ const perf = await intro("buddy seed");
13
+ if (options.class) {
14
+ const { injectGlobalAutoImports } = await import("@stacksjs/server");
15
+ await injectGlobalAutoImports();
16
+ const { runClassSeeders } = await import("@stacksjs/database"), result = await runClassSeeders({ class: options.class });
17
+ await outro(`Class seeders: ran=${result.ran.length}, skipped=${result.skipped.length}`, { startTime: perf, useSeconds: !0 });
18
+ process.exit(result.ran.length > 0 ? ExitCode.Success : ExitCode.FatalError);
19
+ }
20
+ const { injectGlobalAutoImports } = await import("@stacksjs/server");
21
+ await injectGlobalAutoImports();
22
+ const { runClassSeeders, listSeedableModels } = await import("@stacksjs/database"), { path } = await import("@stacksjs/path"), { fs } = await import("@stacksjs/storage"), seedableModels = await listSeedableModels(), seedersDir = path.projectPath("database/seeders"), existingSeederFiles = fs.existsSync(seedersDir) ? new Set(fs.readdirSync(seedersDir).filter((f) => f.endsWith(".ts"))) : new Set, unmigratedModels = seedableModels.filter((m) => !existingSeederFiles.has(`${m.name}Seeder.ts`));
23
+ if (unmigratedModels.length > 0)
24
+ log.warn(`[seed] ${unmigratedModels.length} model(s) declare the deprecated \`useSeeder\` trait but have no class seeder file: ${unmigratedModels.map((m) => m.name).join(", ")}. ` + "The auto-walker has been removed (stacksjs/stacks#1919) \u2014 these models will NOT seed. " + "Run `./buddy seed:scaffold` to codemod a class seeder per model and strip the trait (stacksjs/stacks#1929), then re-run `./buddy seed`.");
25
+ const classResult = await runClassSeeders(), APP_ENV = process.env.APP_ENV || "local", ran = classResult.ran.length;
26
+ await outro(`Seeded your ${APP_ENV} database. Class seeders: ran=${ran}, skipped=${classResult.skipped.length}.`, { startTime: perf, useSeconds: !0 });
27
+ process.exit(ExitCode.Success);
28
+ });
29
+ buddy.command("seed:scaffold", "Generate class seeders for every model with a useSeeder trait (codemod for stacksjs/stacks#1919)").option("--force", "Overwrite existing seeder files", { default: !1 }).option("--dry-run", "Print what would be generated without writing files", { default: !1 }).action(async (options) => {
30
+ const perf = await intro("buddy seed:scaffold");
31
+ try {
32
+ const { scaffoldClassSeedersFromModels } = await import("@stacksjs/database"), result = await scaffoldClassSeedersFromModels({
33
+ force: options.force,
34
+ dryRun: options.dryRun
35
+ }), generated = result.generated.length, alreadyThere = result.skipped.filter((s) => s.reason === "already-exists").length, stripped = result.strippedTrait.length, errors = result.errors.length;
36
+ for (const g of result.generated)
37
+ console.log(` + ${g.model} \u2192 ${g.file}`);
38
+ for (const s of result.skipped.filter((s) => s.reason === "already-exists"))
39
+ console.log(` \xB7 ${s.model}: seeder exists (pass --force to overwrite)`);
40
+ for (const t of result.strippedTrait)
41
+ console.log(` - ${t.model}: removed useSeeder trait from model`);
42
+ for (const t of result.traitStripSkipped)
43
+ log.warn(` ! ${t.model}: useSeeder value couldn't be auto-removed \u2014 strip it manually (${t.file})`);
44
+ for (const e of result.errors)
45
+ log.warn(` ! ${e.model}: ${e.error}`);
46
+ const verb = options.dryRun ? "would generate" : "generated", strippedVerb = options.dryRun ? "would strip" : "stripped";
47
+ await outro(`Seeder scaffold: ${verb} ${generated}, skipped ${alreadyThere} existing, ${strippedVerb} ${stripped} trait(s), ${errors} error(s).`, { startTime: perf, useSeconds: !0 });
48
+ process.exit(errors > 0 && generated === 0 ? ExitCode.FatalError : ExitCode.Success);
49
+ } catch (err) {
50
+ await outro("seed:scaffold failed", { startTime: perf, useSeconds: !0 }, err);
51
+ process.exit(ExitCode.FatalError);
52
+ }
53
+ });
54
+ buddy.command("seed:roles", "Seed default RBAC role packs (admin, dev, client)").alias("roles:seed").action(async () => {
55
+ const perf = await intro("buddy seed:roles");
56
+ try {
57
+ const { seedDefaultRoles } = await import("@stacksjs/auth"), result = await seedDefaultRoles();
58
+ if (result.created.length === 0 && result.skipped.length > 0)
59
+ await outro(`All ${result.skipped.length} default role packs already exist \u2014 nothing to do.`, { startTime: perf, useSeconds: !0 });
60
+ else {
61
+ const createdNames = result.created.map((r) => r.name).join(", ");
62
+ await outro(`Created ${result.created.length} role pack(s): ${createdNames}. Skipped ${result.skipped.length} existing.`, { startTime: perf, useSeconds: !0 });
63
+ }
64
+ process.exit(ExitCode.Success);
65
+ } catch (err) {
66
+ await outro("Failed to seed default roles. Most often: migrations haven't run yet (try `./buddy migrate` first).", { startTime: perf, useSeconds: !0 }, err);
67
+ process.exit(ExitCode.FatalError);
68
+ }
69
+ });
70
+ onUnknownSubcommand(buddy, "seed");
71
+ }
@@ -0,0 +1,25 @@
1
+ import type { CLI } from '@stacksjs/types';
2
+ /**` requests (and any non-GET/HEAD verb) are
3
+ * reverse-proxied to the API process — mirroring the dev views server — so
4
+ * scaffolded `fetch('/api/...')` calls behave identically in production
5
+ * (stacksjs/stacks#1950). The API runs as a separate process
6
+ * (core/actions/src/serve/api.ts), deployed as a second systemd service via
7
+ * the `api` site in config/cloud.ts. Override `API_URL` when the API lives
8
+ * on another host, or `PORT_API` when only the port differs.
9
+ *
10
+ * This is the entry the Hetzner deploy runs as a systemd service
11
+ * (`bun storage/framework/core/buddy/src/cli.ts serve`).
12
+ */
13
+ export declare function serve(buddy: CLI): void;
14
+ /**
15
+ * `buddy serve:api` — boot the production API server (bun-router routes).
16
+ *
17
+ * The twin of `buddy serve`: where that serves the STX frontend, this runs the
18
+ * loopback API the frontend proxies `/api` + non-GET requests to. The entry
19
+ * (`@stacksjs/actions/serve/api`) is resolved through the module graph, so it
20
+ * works whether the framework is vendored at `storage/framework/core` OR only
21
+ * installed under `node_modules/@stacksjs/actions`. This keeps deployments from
22
+ * having to hardcode a `storage/framework/core/...` path in their `start`
23
+ * command — `./buddy serve:api` resolves the framework wherever it lives.
24
+ */
25
+ export declare function serveApi(buddy: CLI): void;