@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.
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +165 -0
- package/dist/commands/about.d.ts +2 -0
- package/dist/commands/about.js +35 -0
- package/dist/commands/add.d.ts +2 -0
- package/dist/commands/add.js +33 -0
- package/dist/commands/auth.d.ts +2 -0
- package/dist/commands/auth.js +68 -0
- package/dist/commands/build.d.ts +2 -0
- package/dist/commands/build.js +153 -0
- package/dist/commands/cd.d.ts +20 -0
- package/dist/commands/cd.js +39 -0
- package/dist/commands/changelog.d.ts +2 -0
- package/dist/commands/changelog.js +32 -0
- package/dist/commands/clean.d.ts +2 -0
- package/dist/commands/clean.js +37 -0
- package/dist/commands/cloud.d.ts +2 -0
- package/dist/commands/cloud.js +503 -0
- package/dist/commands/commit.d.ts +2 -0
- package/dist/commands/commit.js +16 -0
- package/dist/commands/completion.d.ts +2 -0
- package/dist/commands/completion.js +143 -0
- package/dist/commands/config-migrate.d.ts +26 -0
- package/dist/commands/config-migrate.js +94 -0
- package/dist/commands/configure.d.ts +2 -0
- package/dist/commands/configure.js +48 -0
- package/dist/commands/create.d.ts +2 -0
- package/dist/commands/create.js +141 -0
- package/dist/commands/deploy.d.ts +107 -0
- package/dist/commands/deploy.js +1492 -0
- package/dist/commands/dev.d.ts +10 -0
- package/dist/commands/dev.js +915 -0
- package/dist/commands/dns.d.ts +2 -0
- package/dist/commands/dns.js +102 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +285 -0
- package/dist/commands/domains.d.ts +2 -0
- package/dist/commands/domains.js +159 -0
- package/dist/commands/email.d.ts +2 -0
- package/dist/commands/email.js +524 -0
- package/dist/commands/env.d.ts +2 -0
- package/dist/commands/env.js +246 -0
- package/dist/commands/extension.d.ts +8 -0
- package/dist/commands/extension.js +78 -0
- package/dist/commands/features.d.ts +180 -0
- package/dist/commands/features.js +422 -0
- package/dist/commands/fresh.d.ts +2 -0
- package/dist/commands/fresh.js +39 -0
- package/dist/commands/generate.d.ts +2 -0
- package/dist/commands/generate.js +104 -0
- package/dist/commands/http.d.ts +2 -0
- package/dist/commands/http.js +33 -0
- package/dist/commands/index.d.ts +53 -0
- package/dist/commands/index.js +53 -0
- package/dist/commands/install.d.ts +2 -0
- package/dist/commands/install.js +22 -0
- package/dist/commands/key.d.ts +2 -0
- package/dist/commands/key.js +23 -0
- package/dist/commands/lint.d.ts +2 -0
- package/dist/commands/lint.js +46 -0
- package/dist/commands/list.d.ts +2 -0
- package/dist/commands/list.js +108 -0
- package/dist/commands/mail.d.ts +4 -0
- package/dist/commands/mail.js +752 -0
- package/dist/commands/maintenance.d.ts +2 -0
- package/dist/commands/maintenance.js +141 -0
- package/dist/commands/make.d.ts +2 -0
- package/dist/commands/make.js +375 -0
- package/dist/commands/migrate-project.d.ts +2 -0
- package/dist/commands/migrate-project.js +49 -0
- package/dist/commands/migrate.d.ts +4 -0
- package/dist/commands/migrate.js +455 -0
- package/dist/commands/outdated.d.ts +2 -0
- package/dist/commands/outdated.js +19 -0
- package/dist/commands/package.d.ts +2 -0
- package/dist/commands/package.js +17 -0
- package/dist/commands/phone.d.ts +2 -0
- package/dist/commands/phone.js +188 -0
- package/dist/commands/ports.d.ts +2 -0
- package/dist/commands/ports.js +118 -0
- package/dist/commands/prepublish.d.ts +2 -0
- package/dist/commands/prepublish.js +17 -0
- package/dist/commands/projects.d.ts +2 -0
- package/dist/commands/projects.js +29 -0
- package/dist/commands/publish.d.ts +2 -0
- package/dist/commands/publish.js +172 -0
- package/dist/commands/queue.d.ts +2 -0
- package/dist/commands/queue.js +248 -0
- package/dist/commands/release.d.ts +2 -0
- package/dist/commands/release.js +30 -0
- package/dist/commands/route.d.ts +2 -0
- package/dist/commands/route.js +21 -0
- package/dist/commands/saas.d.ts +2 -0
- package/dist/commands/saas.js +25 -0
- package/dist/commands/schedule.d.ts +2 -0
- package/dist/commands/schedule.js +61 -0
- package/dist/commands/search.d.ts +2 -0
- package/dist/commands/search.js +84 -0
- package/dist/commands/seed.d.ts +2 -0
- package/dist/commands/seed.js +71 -0
- package/dist/commands/serve.d.ts +25 -0
- package/dist/commands/serve.js +182 -0
- package/dist/commands/setup.d.ts +12 -0
- package/dist/commands/setup.js +215 -0
- package/dist/commands/share.d.ts +2 -0
- package/dist/commands/share.js +214 -0
- package/dist/commands/sms.d.ts +2 -0
- package/dist/commands/sms.js +328 -0
- package/dist/commands/stacks.d.ts +2 -0
- package/dist/commands/stacks.js +69 -0
- package/dist/commands/telemetry.d.ts +2 -0
- package/dist/commands/telemetry.js +74 -0
- package/dist/commands/test.d.ts +2 -0
- package/dist/commands/test.js +130 -0
- package/dist/commands/tinker.d.ts +2 -0
- package/dist/commands/tinker.js +37 -0
- package/dist/commands/types.d.ts +2 -0
- package/dist/commands/types.js +18 -0
- package/dist/commands/upgrade.d.ts +2 -0
- package/dist/commands/upgrade.js +98 -0
- package/dist/commands/version.d.ts +2 -0
- package/dist/commands/version.js +16 -0
- package/dist/config.d.ts +43 -0
- package/dist/config.js +223 -0
- package/dist/custom-cli.d.ts +1 -0
- package/dist/custom-cli.js +23 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lazy-commands.d.ts +61 -0
- package/dist/lazy-commands.js +188 -0
- package/dist/migrators/index.d.ts +9 -0
- package/dist/migrators/index.js +62 -0
- package/dist/migrators/laravel/index.d.ts +2 -0
- package/dist/migrators/laravel/index.js +148 -0
- package/dist/migrators/laravel/migrations.d.ts +52 -0
- package/dist/migrators/laravel/migrations.js +232 -0
- package/dist/migrators/laravel/models.d.ts +34 -0
- package/dist/migrators/laravel/models.js +134 -0
- package/dist/migrators/rails/index.d.ts +2 -0
- package/dist/migrators/rails/index.js +11 -0
- package/dist/migrators/types.d.ts +44 -0
- package/dist/migrators/types.js +0 -0
- package/package.json +2 -2
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import { getErrorMessage } from "@stacksjs/utils";
|
|
2
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
3
|
+
const TIMEOUT_MS = 30000;
|
|
4
|
+
async function withTimeout(promise, ms = TIMEOUT_MS) {
|
|
5
|
+
let timeoutId;
|
|
6
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
7
|
+
timeoutId = setTimeout(() => reject(Error(`Operation timed out after ${ms}ms`)), ms);
|
|
8
|
+
});
|
|
9
|
+
try {
|
|
10
|
+
return await Promise.race([promise, timeoutPromise]);
|
|
11
|
+
} finally {
|
|
12
|
+
clearTimeout(timeoutId);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function loadAwsCredentials() {
|
|
16
|
+
const envPath = ".env.production";
|
|
17
|
+
if (existsSync(envPath)) {
|
|
18
|
+
const content = readFileSync(envPath, "utf-8");
|
|
19
|
+
for (const line of content.split(`
|
|
20
|
+
`)) {
|
|
21
|
+
const eq = line.indexOf("=");
|
|
22
|
+
if (eq > 0 && !line.startsWith("#"))
|
|
23
|
+
process.env[line.slice(0, eq).trim()] = line.slice(eq + 1).trim();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const descriptions = {
|
|
28
|
+
sms: "SMS service management commands",
|
|
29
|
+
status: "Show SMS service status",
|
|
30
|
+
send: "Send a test SMS",
|
|
31
|
+
setup: "Initialize SMS infrastructure",
|
|
32
|
+
verify: "Verify a sandbox phone number",
|
|
33
|
+
"add-phone": "Add a phone to sandbox for testing"
|
|
34
|
+
};
|
|
35
|
+
export function sms(buddy) {
|
|
36
|
+
buddy.command("sms", descriptions.sms).action(async () => {
|
|
37
|
+
console.log(`
|
|
38
|
+
\uD83D\uDCF1 SMS Service Commands
|
|
39
|
+
`);
|
|
40
|
+
console.log(" buddy sms:status - Show SMS service status");
|
|
41
|
+
console.log(" buddy sms:send - Send a test SMS");
|
|
42
|
+
console.log(" buddy sms:setup - Initialize SMS infrastructure");
|
|
43
|
+
console.log(" buddy sms:add-phone - Add a phone to sandbox for testing");
|
|
44
|
+
console.log(" buddy sms:verify - Verify a sandbox phone number with OTP");
|
|
45
|
+
console.log("");
|
|
46
|
+
process.exit(0);
|
|
47
|
+
});
|
|
48
|
+
buddy.command("sms:status", descriptions.status).action(async () => {
|
|
49
|
+
console.log(`
|
|
50
|
+
\uD83D\uDCF1 SMS Service Status
|
|
51
|
+
`);
|
|
52
|
+
loadAwsCredentials();
|
|
53
|
+
try {
|
|
54
|
+
const { getSmsInfrastructureStatus } = await import("@stacksjs/ts-cloud"), status = await withTimeout(getSmsInfrastructureStatus({
|
|
55
|
+
region: process.env.AWS_REGION || "us-east-1",
|
|
56
|
+
accountName: "stacks"
|
|
57
|
+
}), 30000);
|
|
58
|
+
console.log("Phone Numbers:");
|
|
59
|
+
if (status.phoneNumbers.length === 0) {
|
|
60
|
+
console.log(" No phone numbers provisioned");
|
|
61
|
+
console.log(" Run `buddy sms:setup` to provision a phone number");
|
|
62
|
+
} else
|
|
63
|
+
for (const phone of status.phoneNumbers) {
|
|
64
|
+
console.log(` ${phone.phoneNumber}`);
|
|
65
|
+
console.log(` Status: ${phone.status}`);
|
|
66
|
+
console.log(` Two-Way: ${phone.twoWayEnabled ? "Enabled" : "Disabled"}`);
|
|
67
|
+
console.log(` Capabilities: ${phone.capabilities.join(", ") || "N/A"}`);
|
|
68
|
+
}
|
|
69
|
+
console.log(`
|
|
70
|
+
Account:`);
|
|
71
|
+
console.log(` Sandbox Status: ${status.sandboxStatus === "IN_SANDBOX" ? "\u26A0\uFE0F In Sandbox" : "\u2705 Production"}`);
|
|
72
|
+
console.log(` Spending Limit: $${status.spendingLimit}/month`);
|
|
73
|
+
console.log(`
|
|
74
|
+
SNS Topics:`);
|
|
75
|
+
if (status.topics.length === 0)
|
|
76
|
+
console.log(" No SMS topics configured");
|
|
77
|
+
else
|
|
78
|
+
for (const topic of status.topics)
|
|
79
|
+
console.log(` ${topic.name}`);
|
|
80
|
+
if (status.sandboxStatus === "IN_SANDBOX") {
|
|
81
|
+
console.log(`
|
|
82
|
+
\uD83D\uDCA1 To exit sandbox mode:`);
|
|
83
|
+
console.log(" Run `buddy sms:setup` to file a support request");
|
|
84
|
+
console.log(" Or visit AWS Console > End User Messaging > SMS > Request production access");
|
|
85
|
+
}
|
|
86
|
+
if (status.phoneNumbers.some((p) => p.status === "PENDING"))
|
|
87
|
+
console.log(`
|
|
88
|
+
\u23F3 Note: Phone number verification can take 24-72 hours for toll-free numbers`);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.error("Error checking status:", getErrorMessage(error));
|
|
91
|
+
try {
|
|
92
|
+
const { PinpointSmsVoiceClient } = await import("@stacksjs/ts-cloud"), smsClient = new PinpointSmsVoiceClient(process.env.AWS_REGION || "us-east-1"), activeNumbers = (await withTimeout(smsClient.describePhoneNumbers({}))).PhoneNumbers?.filter((p) => p.Status === "ACTIVE" || p.Status === "PENDING");
|
|
93
|
+
if (activeNumbers && activeNumbers.length > 0) {
|
|
94
|
+
console.log(`
|
|
95
|
+
\u2705 SMS Service Active (End User Messaging)`);
|
|
96
|
+
for (const phone of activeNumbers)
|
|
97
|
+
console.log(` Phone: ${phone.PhoneNumber} (${phone.Status})`);
|
|
98
|
+
} else {
|
|
99
|
+
console.log(`
|
|
100
|
+
\u274C SMS service not deployed.`);
|
|
101
|
+
console.log(" Run `buddy sms:setup` to initialize");
|
|
102
|
+
}
|
|
103
|
+
} catch {
|
|
104
|
+
console.log(`
|
|
105
|
+
\u274C Could not connect to AWS. Check credentials.`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
process.exit(0);
|
|
109
|
+
});
|
|
110
|
+
buddy.command("sms:send <phone> [message]", descriptions.send).option("--template <name>", "Use a message template").action(async (phone, message, options) => {
|
|
111
|
+
const msg = message || "Test message from Stacks";
|
|
112
|
+
console.log(`
|
|
113
|
+
\uD83D\uDCF1 Sending SMS to ${phone}...
|
|
114
|
+
`);
|
|
115
|
+
loadAwsCredentials();
|
|
116
|
+
try {
|
|
117
|
+
const { SMSClient } = await import("@stacksjs/ts-cloud"), smsClient = new SMSClient({
|
|
118
|
+
region: process.env.AWS_REGION || "us-east-1"
|
|
119
|
+
}), smsConfig = await import("@stacksjs/config").then((m) => m.config?.sms);
|
|
120
|
+
let originationNumber = smsConfig?.originationNumber;
|
|
121
|
+
if (!originationNumber) {
|
|
122
|
+
const { PinpointSmsVoiceClient } = await import("@stacksjs/ts-cloud"), pinpointV2 = new PinpointSmsVoiceClient(process.env.AWS_REGION || "us-east-1");
|
|
123
|
+
originationNumber = (await withTimeout(pinpointV2.describePhoneNumbers({}))).PhoneNumbers?.find((p) => p.Status === "ACTIVE")?.PhoneNumber;
|
|
124
|
+
}
|
|
125
|
+
let finalMessage = msg;
|
|
126
|
+
if (options?.template && smsConfig?.templates) {
|
|
127
|
+
const template = smsConfig.templates.find((t) => t.name === options.template);
|
|
128
|
+
if (template) {
|
|
129
|
+
finalMessage = template.body;
|
|
130
|
+
console.log(`Using template: ${options.template}`);
|
|
131
|
+
} else
|
|
132
|
+
console.log(`Template "${options.template}" not found. Using default message.`);
|
|
133
|
+
}
|
|
134
|
+
const result = await withTimeout(smsClient.send({
|
|
135
|
+
to: phone,
|
|
136
|
+
message: finalMessage,
|
|
137
|
+
originationNumber
|
|
138
|
+
}), 30000);
|
|
139
|
+
if (result.messageId) {
|
|
140
|
+
console.log("\u2705 SMS sent successfully!");
|
|
141
|
+
console.log(` Message ID: ${result.messageId}`);
|
|
142
|
+
if (originationNumber)
|
|
143
|
+
console.log(` From: ${originationNumber}`);
|
|
144
|
+
} else
|
|
145
|
+
console.log("\u274C SMS send failed");
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error("Error sending SMS:", getErrorMessage(error));
|
|
148
|
+
if (getErrorMessage(error).includes("not found") || getErrorMessage(error).includes("No phone")) {
|
|
149
|
+
console.log(`
|
|
150
|
+
\uD83D\uDCA1 No origination phone number available.`);
|
|
151
|
+
console.log(" Run `buddy sms:setup` to provision a phone number.");
|
|
152
|
+
} else if (getErrorMessage(error).includes("not authorized"))
|
|
153
|
+
console.log(`
|
|
154
|
+
\uD83D\uDCA1 Make sure your AWS account has SMS permissions.`);
|
|
155
|
+
else if (getErrorMessage(error).includes("sandbox"))
|
|
156
|
+
console.log("\n\uD83D\uDCA1 Your account is in SMS sandbox. Run `buddy sms:setup` to request production access.");
|
|
157
|
+
else if (getErrorMessage(error).includes("PENDING")) {
|
|
158
|
+
console.log(`
|
|
159
|
+
\uD83D\uDCA1 Phone number is still pending verification (24-72 hours for toll-free).`);
|
|
160
|
+
console.log(" Run `buddy sms:status` to check current status.");
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
process.exit(0);
|
|
164
|
+
});
|
|
165
|
+
buddy.command("sms:setup", descriptions.setup).option("--dry-run", "Show what would be done without making changes").action(async (options) => {
|
|
166
|
+
console.log(`
|
|
167
|
+
\uD83D\uDCF1 SMS Service Setup
|
|
168
|
+
`);
|
|
169
|
+
loadAwsCredentials();
|
|
170
|
+
try {
|
|
171
|
+
const smsConfig = await import("@stacksjs/config").then((m) => m.config?.sms);
|
|
172
|
+
if (!smsConfig?.enabled) {
|
|
173
|
+
console.log("SMS is disabled in config/sms.ts");
|
|
174
|
+
console.log(`
|
|
175
|
+
To enable SMS:`);
|
|
176
|
+
console.log("1. Edit config/sms.ts and set enabled: true");
|
|
177
|
+
console.log("2. Run `buddy sms:setup` again");
|
|
178
|
+
process.exit(0);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (options.dryRun) {
|
|
182
|
+
console.log("[Dry Run] Would set up SMS infrastructure with config:");
|
|
183
|
+
console.log(` Provider: ${smsConfig.provider}`);
|
|
184
|
+
console.log(` Country: ${smsConfig.defaultCountryCode}`);
|
|
185
|
+
console.log(` Message Type: ${smsConfig.messageType}`);
|
|
186
|
+
console.log(` Monthly Limit: $${smsConfig.maxSpendPerMonth || 1}`);
|
|
187
|
+
console.log(` Two-Way: ${smsConfig.twoWay?.enabled ? "Enabled" : "Disabled"}`);
|
|
188
|
+
console.log(` Inbox: ${smsConfig.inbox?.enabled ? "Enabled" : "Disabled"}`);
|
|
189
|
+
process.exit(0);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
console.log(`Setting up SMS infrastructure...
|
|
193
|
+
`);
|
|
194
|
+
const { createSmsInfrastructure } = await import("@stacksjs/ts-cloud"), result = await withTimeout(createSmsInfrastructure({
|
|
195
|
+
enabled: smsConfig.enabled,
|
|
196
|
+
provider: smsConfig.provider || "pinpoint",
|
|
197
|
+
originationNumber: smsConfig.originationNumber,
|
|
198
|
+
defaultCountryCode: smsConfig.defaultCountryCode || "US",
|
|
199
|
+
messageType: smsConfig.messageType || "TRANSACTIONAL",
|
|
200
|
+
maxSpendPerMonth: smsConfig.maxSpendPerMonth,
|
|
201
|
+
inbox: smsConfig.inbox,
|
|
202
|
+
twoWay: smsConfig.twoWay,
|
|
203
|
+
optOut: smsConfig.optOut || { enabled: !0, keywords: ["STOP"] }
|
|
204
|
+
}), 120000);
|
|
205
|
+
if (result.success) {
|
|
206
|
+
console.log(`
|
|
207
|
+
\u2705 SMS infrastructure setup complete!
|
|
208
|
+
`);
|
|
209
|
+
if (result.phoneNumber)
|
|
210
|
+
console.log(` Phone Number: ${result.phoneNumber} (${result.phoneNumberStatus})`);
|
|
211
|
+
if (result.twoWayTopicArn)
|
|
212
|
+
console.log(` Two-Way Topic: ${result.twoWayTopicArn}`);
|
|
213
|
+
if (result.inboxBucket)
|
|
214
|
+
console.log(` Inbox: s3://${result.inboxBucket}/${result.inboxPrefix}`);
|
|
215
|
+
console.log(` Spending Limit: $${result.spendingLimit}/month`);
|
|
216
|
+
console.log(` Sandbox Status: ${result.sandboxStatus}`);
|
|
217
|
+
if (result.supportCaseId) {
|
|
218
|
+
console.log(`
|
|
219
|
+
\uD83D\uDCCB Support Case: ${result.supportCaseId}`);
|
|
220
|
+
console.log(" A support ticket has been filed for sandbox exit.");
|
|
221
|
+
}
|
|
222
|
+
if (result.warnings.length > 0) {
|
|
223
|
+
console.log(`
|
|
224
|
+
\u26A0\uFE0F Warnings:`);
|
|
225
|
+
for (const warning of result.warnings)
|
|
226
|
+
console.log(` - ${warning}`);
|
|
227
|
+
}
|
|
228
|
+
console.log(`
|
|
229
|
+
\uD83D\uDCA1 Next steps:`);
|
|
230
|
+
console.log(' - Run `buddy sms:send +1234567890 "Test"` to send a test SMS');
|
|
231
|
+
console.log(" - Run `buddy sms:status` to check current status");
|
|
232
|
+
} else {
|
|
233
|
+
console.log(`
|
|
234
|
+
\u274C SMS setup completed with errors:
|
|
235
|
+
`);
|
|
236
|
+
for (const error of result.errors)
|
|
237
|
+
console.log(` - ${error}`);
|
|
238
|
+
}
|
|
239
|
+
} catch (error) {
|
|
240
|
+
console.error("Error setting up SMS:", getErrorMessage(error));
|
|
241
|
+
if (getErrorMessage(error).includes("timeout")) {
|
|
242
|
+
console.log(`
|
|
243
|
+
\uD83D\uDCA1 Setup timed out. Some operations may have completed.`);
|
|
244
|
+
console.log(" Run `buddy sms:status` to check current status.");
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
process.exit(0);
|
|
248
|
+
});
|
|
249
|
+
buddy.command("sms:add-phone <phone>", descriptions["add-phone"]).action(async (phone) => {
|
|
250
|
+
console.log(`
|
|
251
|
+
\uD83D\uDCF1 Adding ${phone} to SMS sandbox...
|
|
252
|
+
`);
|
|
253
|
+
loadAwsCredentials();
|
|
254
|
+
try {
|
|
255
|
+
const { PinpointSmsVoiceClient } = await import("@stacksjs/ts-cloud"), smsClient = new PinpointSmsVoiceClient(process.env.AWS_REGION || "us-east-1"), result = await withTimeout(smsClient.addSandboxPhone(phone), 30000);
|
|
256
|
+
if (result.status === "VERIFIED") {
|
|
257
|
+
console.log("\u2705 Phone is already verified!");
|
|
258
|
+
console.log(` ID: ${result.verifiedDestinationNumberId}`);
|
|
259
|
+
} else if (result.status === "PENDING") {
|
|
260
|
+
console.log("\uD83D\uDCE8 Verification code sent to phone!");
|
|
261
|
+
console.log(` ID: ${result.verifiedDestinationNumberId}`);
|
|
262
|
+
console.log(`
|
|
263
|
+
\uD83D\uDCA1 Next step:`);
|
|
264
|
+
console.log(` Run: buddy sms:verify ${result.verifiedDestinationNumberId} <code>`);
|
|
265
|
+
console.log(" Replace <code> with the 6-digit code you received");
|
|
266
|
+
} else {
|
|
267
|
+
console.log("\u2753 Unexpected status:", result.status);
|
|
268
|
+
console.log(" Message:", result.message);
|
|
269
|
+
}
|
|
270
|
+
} catch (error) {
|
|
271
|
+
console.error("Error adding phone:", getErrorMessage(error));
|
|
272
|
+
if (getErrorMessage(error).includes("already exists")) {
|
|
273
|
+
console.log(`
|
|
274
|
+
\uD83D\uDCA1 Phone number may already be in the sandbox.`);
|
|
275
|
+
console.log(" Run `buddy sms:status` to check sandbox numbers.");
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
process.exit(0);
|
|
279
|
+
});
|
|
280
|
+
buddy.command("sms:verify <id> <code>", descriptions.verify).action(async (id, code) => {
|
|
281
|
+
console.log(`
|
|
282
|
+
\uD83D\uDCF1 Verifying sandbox phone...
|
|
283
|
+
`);
|
|
284
|
+
loadAwsCredentials();
|
|
285
|
+
try {
|
|
286
|
+
const { PinpointSmsVoiceClient } = await import("@stacksjs/ts-cloud"), smsClient = new PinpointSmsVoiceClient(process.env.AWS_REGION || "us-east-1"), result = await withTimeout(smsClient.verifySandboxPhone(id, code), 30000);
|
|
287
|
+
if (result.success) {
|
|
288
|
+
console.log("\u2705 Phone verified successfully!");
|
|
289
|
+
console.log(` Phone: ${result.phoneNumber}`);
|
|
290
|
+
console.log(`
|
|
291
|
+
\uD83D\uDCA1 You can now send SMS to this phone while in sandbox mode.`);
|
|
292
|
+
console.log(` Run: buddy sms:send ${result.phoneNumber} "Test message"`);
|
|
293
|
+
} else {
|
|
294
|
+
console.log("\u274C Verification failed");
|
|
295
|
+
console.log(` Error: ${result.message}`);
|
|
296
|
+
console.log(`
|
|
297
|
+
\uD83D\uDCA1 Make sure you entered the correct 6-digit code.`);
|
|
298
|
+
console.log(" Run `buddy sms:add-phone <phone>` to resend the code.");
|
|
299
|
+
}
|
|
300
|
+
} catch (error) {
|
|
301
|
+
console.error("Error verifying phone:", getErrorMessage(error));
|
|
302
|
+
}
|
|
303
|
+
process.exit(0);
|
|
304
|
+
});
|
|
305
|
+
buddy.command("sms:sandbox-list", "List sandbox verified phone numbers").action(async () => {
|
|
306
|
+
console.log(`
|
|
307
|
+
\uD83D\uDCF1 Sandbox Verified Numbers
|
|
308
|
+
`);
|
|
309
|
+
loadAwsCredentials();
|
|
310
|
+
try {
|
|
311
|
+
const { PinpointSmsVoiceClient } = await import("@stacksjs/ts-cloud"), smsClient = new PinpointSmsVoiceClient(process.env.AWS_REGION || "us-east-1"), phones = await withTimeout(smsClient.listSandboxPhones(), 30000);
|
|
312
|
+
if (phones.length === 0) {
|
|
313
|
+
console.log("No verified sandbox numbers yet.");
|
|
314
|
+
console.log(`
|
|
315
|
+
To add a phone:`);
|
|
316
|
+
console.log(" buddy sms:add-phone +1234567890");
|
|
317
|
+
} else
|
|
318
|
+
for (const phone of phones) {
|
|
319
|
+
const statusIcon = phone.status === "VERIFIED" ? "\u2705" : "\u23F3";
|
|
320
|
+
console.log(` ${statusIcon} ${phone.phoneNumber} (${phone.status})`);
|
|
321
|
+
console.log(` ID: ${phone.id}`);
|
|
322
|
+
}
|
|
323
|
+
} catch (error) {
|
|
324
|
+
console.error("Error listing sandbox phones:", getErrorMessage(error));
|
|
325
|
+
}
|
|
326
|
+
process.exit(0);
|
|
327
|
+
});
|
|
328
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { installStack, listStacks, uninstallStack } from "@stacksjs/actions";
|
|
3
|
+
import { intro, italic, onUnknownSubcommand, outro } from "@stacksjs/cli";
|
|
4
|
+
import { log } from "@stacksjs/logging";
|
|
5
|
+
import { ExitCode } from "@stacksjs/types";
|
|
6
|
+
export function stacks(buddy) {
|
|
7
|
+
const descriptions = {
|
|
8
|
+
install: "Install a stack into your project",
|
|
9
|
+
uninstall: "Uninstall a stack from your project",
|
|
10
|
+
list: "List available and installed stacks",
|
|
11
|
+
force: "Force overwrite existing files",
|
|
12
|
+
dryRun: "Show what would be installed without making changes",
|
|
13
|
+
conflict: "Conflict resolution strategy: skip, overwrite, or backup",
|
|
14
|
+
verbose: "Enable verbose output"
|
|
15
|
+
};
|
|
16
|
+
buddy.command("stack:install <name>", descriptions.install).option("--force", descriptions.force, { default: !1 }).option("--dry-run", descriptions.dryRun, { default: !1 }).option("--conflict <strategy>", descriptions.conflict, { default: "skip" }).option("--verbose", descriptions.verbose, { default: !1 }).example("buddy stack:install @stacksjs/blog").example("buddy stack:install blog --force").example("buddy stack:install blog --conflict backup --verbose").example("buddy stack:install blog --dry-run").action(async (name, options) => {
|
|
17
|
+
const perf = await intro("buddy stack:install");
|
|
18
|
+
if (!name) {
|
|
19
|
+
log.error("You need to specify a stack name.");
|
|
20
|
+
log.info("Example: buddy stack:install @stacksjs/blog");
|
|
21
|
+
process.exit(ExitCode.FatalError);
|
|
22
|
+
}
|
|
23
|
+
if (!await installStack({
|
|
24
|
+
name,
|
|
25
|
+
force: options.force,
|
|
26
|
+
dryRun: options.dryRun,
|
|
27
|
+
conflict: options.conflict || "skip",
|
|
28
|
+
verbose: options.verbose
|
|
29
|
+
}) && !options.dryRun) {
|
|
30
|
+
await outro("Failed to install stack", { startTime: perf, useSeconds: !0 });
|
|
31
|
+
process.exit(ExitCode.FatalError);
|
|
32
|
+
}
|
|
33
|
+
await outro(`Stack ${italic(name)} installed.`, { startTime: perf, useSeconds: !0 });
|
|
34
|
+
process.exit(ExitCode.Success);
|
|
35
|
+
});
|
|
36
|
+
buddy.command("stack:uninstall <name>", descriptions.uninstall).option("--force", descriptions.force, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).example("buddy stack:uninstall blog").example("buddy stack:uninstall blog --force").action(async (name, options) => {
|
|
37
|
+
const perf = await intro("buddy stack:uninstall");
|
|
38
|
+
if (!name) {
|
|
39
|
+
log.error("You need to specify a stack name.");
|
|
40
|
+
process.exit(ExitCode.FatalError);
|
|
41
|
+
}
|
|
42
|
+
if (!await uninstallStack({
|
|
43
|
+
name,
|
|
44
|
+
force: options.force,
|
|
45
|
+
verbose: options.verbose
|
|
46
|
+
})) {
|
|
47
|
+
await outro("Failed to uninstall stack", { startTime: perf, useSeconds: !0 });
|
|
48
|
+
process.exit(ExitCode.FatalError);
|
|
49
|
+
}
|
|
50
|
+
await outro(`Stack ${italic(name)} uninstalled.`, { startTime: perf, useSeconds: !0 });
|
|
51
|
+
process.exit(ExitCode.Success);
|
|
52
|
+
});
|
|
53
|
+
buddy.command("stack:list", descriptions.list).alias("stack:ls").example("buddy stack:list").action(async () => {
|
|
54
|
+
const perf = await intro("buddy stack:list"), entries = await listStacks();
|
|
55
|
+
if (entries.length === 0)
|
|
56
|
+
log.info("No stacks found. Install one with: buddy stack:install <name>");
|
|
57
|
+
else {
|
|
58
|
+
log.info(`Found ${entries.length} stack(s):`);
|
|
59
|
+
log.info("");
|
|
60
|
+
for (const entry of entries) {
|
|
61
|
+
const status = entry.installed ? "[installed]" : "[available]", desc = entry.description ? ` - ${entry.description}` : "", files = entry.fileCount ? ` (${entry.fileCount} files)` : "";
|
|
62
|
+
log.info(` ${entry.name} ${italic(`v${entry.version}`)} ${status}${files}${desc}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
await outro("", { startTime: perf, useSeconds: !0 });
|
|
66
|
+
process.exit(ExitCode.Success);
|
|
67
|
+
});
|
|
68
|
+
onUnknownSubcommand(buddy, "stack");
|
|
69
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { bold, dim, green, intro, log, onUnknownSubcommand } from "@stacksjs/cli";
|
|
3
|
+
const telemetry = {
|
|
4
|
+
async enable() {},
|
|
5
|
+
async disable() {},
|
|
6
|
+
async status() {
|
|
7
|
+
return {
|
|
8
|
+
enabled: !1,
|
|
9
|
+
doNotTrack: !!process.env.DO_NOT_TRACK,
|
|
10
|
+
eventsQueued: 0,
|
|
11
|
+
lastSent: null
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
export function telemetryCommand(buddy) {
|
|
16
|
+
buddy.command("telemetry", "Manage telemetry settings").option("--enable", "Enable telemetry").option("--disable", "Disable telemetry").option("--status", "Show telemetry status").example("buddy telemetry --status").example("buddy telemetry --enable").example("buddy telemetry --disable").action(async (options) => {
|
|
17
|
+
log.debug("Running `buddy telemetry` ...", options);
|
|
18
|
+
await intro("buddy telemetry");
|
|
19
|
+
try {
|
|
20
|
+
if (options.enable) {
|
|
21
|
+
await telemetry.enable();
|
|
22
|
+
log.success("Telemetry enabled");
|
|
23
|
+
log.info("");
|
|
24
|
+
log.info(dim("Anonymous usage statistics will be collected"));
|
|
25
|
+
log.info(dim("to help improve Buddy CLI."));
|
|
26
|
+
log.info("");
|
|
27
|
+
log.info(dim("You can disable telemetry anytime with:"));
|
|
28
|
+
log.info(dim(" buddy telemetry --disable"));
|
|
29
|
+
log.info("");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (options.disable) {
|
|
33
|
+
await telemetry.disable();
|
|
34
|
+
log.success("Telemetry disabled");
|
|
35
|
+
log.info("");
|
|
36
|
+
log.info(dim("No usage statistics will be collected."));
|
|
37
|
+
log.info("");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const status = await telemetry.status();
|
|
41
|
+
log.info("");
|
|
42
|
+
log.info(green(bold("Telemetry Status")));
|
|
43
|
+
log.info(dim("\u2500".repeat(50)));
|
|
44
|
+
log.info("");
|
|
45
|
+
log.info(bold("Configuration:"));
|
|
46
|
+
log.info(` Enabled: ${dim(status.enabled ? "Yes" : "No")}`);
|
|
47
|
+
log.info(` DO_NOT_TRACK: ${dim(status.doNotTrack ? "Yes (respected)" : "No")}`);
|
|
48
|
+
log.info(` Events queued: ${dim(status.eventsQueued)}`);
|
|
49
|
+
if (status.lastSent) {
|
|
50
|
+
const lastSent = new Date(status.lastSent);
|
|
51
|
+
log.info(` Last sent: ${dim(lastSent.toLocaleString())}`);
|
|
52
|
+
} else
|
|
53
|
+
log.info(` Last sent: ${dim("Never")}`);
|
|
54
|
+
log.info("");
|
|
55
|
+
log.info(bold("Privacy:"));
|
|
56
|
+
log.info(` ${dim("\u2022 Opt-in only (disabled by default)")}`);
|
|
57
|
+
log.info(` ${dim("\u2022 No personal information collected")}`);
|
|
58
|
+
log.info(` ${dim("\u2022 Anonymous user IDs only")}`);
|
|
59
|
+
log.info(` ${dim("\u2022 Respects DO_NOT_TRACK environment variable")}`);
|
|
60
|
+
log.info("");
|
|
61
|
+
if (!status.enabled) {
|
|
62
|
+
log.info(dim("To enable telemetry: buddy telemetry --enable"));
|
|
63
|
+
log.info("");
|
|
64
|
+
} else {
|
|
65
|
+
log.info(dim("To disable telemetry: buddy telemetry --disable"));
|
|
66
|
+
log.info("");
|
|
67
|
+
}
|
|
68
|
+
} catch (error) {
|
|
69
|
+
log.error("Failed to manage telemetry:", error);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
onUnknownSubcommand(buddy, "telemetry");
|
|
74
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
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 { projectPath } from "@stacksjs/path";
|
|
6
|
+
import { ExitCode } from "@stacksjs/types";
|
|
7
|
+
export function test(buddy) {
|
|
8
|
+
const descriptions = {
|
|
9
|
+
command: "Runs your test suite",
|
|
10
|
+
types: "Typechecks your codebase",
|
|
11
|
+
coverage: "Generates a test coverage report",
|
|
12
|
+
ui: "Runs your test suite in the browser",
|
|
13
|
+
unit: "Runs your unit tests",
|
|
14
|
+
feature: "Runs your feature tests",
|
|
15
|
+
showReport: "Show the test report",
|
|
16
|
+
project: "Target a specific project",
|
|
17
|
+
verbose: "Enable verbose output"
|
|
18
|
+
};
|
|
19
|
+
buddy.command("test", descriptions.command).option("-f, --feature", descriptions.feature, { default: !1 }).option("-u, --unit", descriptions.unit, { default: !1 }).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !0 }).action(async (options) => {
|
|
20
|
+
const perf = await intro("buddy test");
|
|
21
|
+
if (options.feature && options.unit) {
|
|
22
|
+
const result = await runAction(Action.Test, {
|
|
23
|
+
...options,
|
|
24
|
+
cwd: projectPath()
|
|
25
|
+
});
|
|
26
|
+
if (result.isErr) {
|
|
27
|
+
await outro("While running `buddy test`, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
|
|
28
|
+
process.exit(ExitCode.FatalError);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (options.feature && !options.unit) {
|
|
32
|
+
log.info("Running Feature tests...");
|
|
33
|
+
const result = await runAction(Action.TestFeature, {
|
|
34
|
+
...options,
|
|
35
|
+
cwd: projectPath()
|
|
36
|
+
});
|
|
37
|
+
if (result.isErr) {
|
|
38
|
+
await outro("While running `buddy test`, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
|
|
39
|
+
process.exit(ExitCode.FatalError);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (!options.feature && options.unit) {
|
|
43
|
+
log.info("Running Unit tests...");
|
|
44
|
+
const result = await runAction(Action.TestUnit, {
|
|
45
|
+
...options,
|
|
46
|
+
cwd: projectPath()
|
|
47
|
+
});
|
|
48
|
+
if (result.isErr) {
|
|
49
|
+
await outro("While running `buddy test`, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
|
|
50
|
+
process.exit(ExitCode.FatalError);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (!options.feature && !options.unit) {
|
|
54
|
+
const result = await runAction(Action.Test, {
|
|
55
|
+
...options,
|
|
56
|
+
cwd: projectPath()
|
|
57
|
+
});
|
|
58
|
+
if (result.isErr) {
|
|
59
|
+
await outro("While running `buddy test`, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
|
|
60
|
+
process.exit(ExitCode.FatalError);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
await outro("Finished running tests", {
|
|
64
|
+
startTime: perf,
|
|
65
|
+
useSeconds: !0
|
|
66
|
+
});
|
|
67
|
+
process.exit(ExitCode.Success);
|
|
68
|
+
});
|
|
69
|
+
buddy.command("test:unit", descriptions.unit).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
|
|
70
|
+
const perf = await intro("buddy test:unit"), result = await runAction(Action.TestUnit, {
|
|
71
|
+
...options,
|
|
72
|
+
verbose: !0,
|
|
73
|
+
cwd: projectPath()
|
|
74
|
+
});
|
|
75
|
+
if (result.isErr) {
|
|
76
|
+
await outro("While running `buddy test:unit`, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
|
|
77
|
+
process.exit(ExitCode.FatalError);
|
|
78
|
+
}
|
|
79
|
+
await outro("Finished running unit tests", {
|
|
80
|
+
startTime: perf,
|
|
81
|
+
useSeconds: !0
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
buddy.command("test:feature", descriptions.feature).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
|
|
85
|
+
const perf = await intro("buddy test:feature"), result = await runAction(Action.TestFeature, {
|
|
86
|
+
...options,
|
|
87
|
+
verbose: !0,
|
|
88
|
+
cwd: projectPath()
|
|
89
|
+
});
|
|
90
|
+
if (result.isErr) {
|
|
91
|
+
await outro("While running `buddy test:feature`, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
|
|
92
|
+
process.exit(ExitCode.FatalError);
|
|
93
|
+
}
|
|
94
|
+
await outro("Finished running feature tests", {
|
|
95
|
+
startTime: perf,
|
|
96
|
+
useSeconds: !0
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
buddy.command("test:ui", descriptions.command).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
|
|
100
|
+
const perf = await intro("buddy test:ui"), result = await runAction(Action.TestUi, {
|
|
101
|
+
...options,
|
|
102
|
+
verbose: !0,
|
|
103
|
+
cwd: projectPath()
|
|
104
|
+
});
|
|
105
|
+
if (result.isErr) {
|
|
106
|
+
await outro("While running `buddy test:ui`, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
|
|
107
|
+
process.exit(ExitCode.FatalError);
|
|
108
|
+
}
|
|
109
|
+
await outro("Finished running tests in the browser", {
|
|
110
|
+
startTime: perf,
|
|
111
|
+
useSeconds: !0
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
buddy.command("test:types", descriptions.types).alias("typecheck").option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
|
|
115
|
+
const perf = await intro("buddy test:types"), result = await runAction(Action.Typecheck, {
|
|
116
|
+
...options,
|
|
117
|
+
verbose: !0,
|
|
118
|
+
cwd: projectPath()
|
|
119
|
+
});
|
|
120
|
+
if (result.isErr) {
|
|
121
|
+
await outro("While running `buddy test:types`, there was an issue", { startTime: perf, useSeconds: !0 }, result.error);
|
|
122
|
+
process.exit(ExitCode.FatalError);
|
|
123
|
+
}
|
|
124
|
+
await outro("Finished running typecheck", {
|
|
125
|
+
startTime: perf,
|
|
126
|
+
useSeconds: !0
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
onUnknownSubcommand(buddy, "test");
|
|
130
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { intro, log, onUnknownSubcommand, outro } from "@stacksjs/cli";
|
|
3
|
+
import { ExitCode } from "@stacksjs/types";
|
|
4
|
+
export function tinker(buddy) {
|
|
5
|
+
const descriptions = {
|
|
6
|
+
tinker: "Interactive REPL with Stacks framework preloaded",
|
|
7
|
+
eval: "Evaluate a single expression and exit",
|
|
8
|
+
print: "Evaluate, print, and exit",
|
|
9
|
+
noBanner: "Skip the welcome banner",
|
|
10
|
+
preload: "Additional modules to preload (comma-separated)",
|
|
11
|
+
project: "Target a specific project",
|
|
12
|
+
verbose: "Enable verbose output"
|
|
13
|
+
};
|
|
14
|
+
buddy.command("tinker", descriptions.tinker).option("-e, --eval [expression]", descriptions.eval, { default: "" }).option("--print [expression]", descriptions.print, { default: "" }).option("--no-banner", descriptions.noBanner, { default: !1 }).option("--preload [modules]", descriptions.preload, { default: "" }).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
|
|
15
|
+
log.debug("Running `buddy tinker` ...", options);
|
|
16
|
+
const perf = await intro("buddy tinker");
|
|
17
|
+
try {
|
|
18
|
+
const { startTinker } = await import("@stacksjs/tinker"), preloadModules = typeof options.preload === "string" && options.preload ? options.preload.split(",").map((m) => m.trim()) : void 0;
|
|
19
|
+
if ((await startTinker({
|
|
20
|
+
eval: typeof options.eval === "string" && options.eval ? options.eval : void 0,
|
|
21
|
+
print: typeof options.print === "string" && options.print ? options.print : void 0,
|
|
22
|
+
banner: options.noBanner !== !0,
|
|
23
|
+
preload: preloadModules,
|
|
24
|
+
verbose: options.verbose === !0
|
|
25
|
+
})).exitCode !== 0) {
|
|
26
|
+
await outro("Tinker session ended with errors", { startTime: perf, useSeconds: !0 });
|
|
27
|
+
process.exit(ExitCode.FatalError);
|
|
28
|
+
}
|
|
29
|
+
await outro("Tinker session ended.", { startTime: perf, useSeconds: !0 });
|
|
30
|
+
process.exit(ExitCode.Success);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
await outro("While running the tinker command, there was an issue", { startTime: perf, useSeconds: !0 }, error instanceof Error ? error : void 0);
|
|
33
|
+
process.exit(ExitCode.FatalError);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
onUnknownSubcommand(buddy, "tinker");
|
|
37
|
+
}
|