@wraps.dev/cli 2.24.0 → 2.25.1
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/README.md +3 -1
- package/dist/api-lambda.zip +0 -0
- package/dist/cli.js +105 -48
- package/dist/cli.js.map +1 -1
- package/dist/console/assets/{index-5HGEQ1Ul.js → index-P0x5JCC4.js} +1 -1
- package/dist/console/index.html +1 -1
- package/dist/lambda/event-processor/.bundled +1 -1
- package/dist/lambda/inbound-processor/.bundled +1 -1
- package/dist/lambda/sms-event-processor/.bundled +1 -1
- package/dist/selfhost-migrations/0063_greedy_legion.sql +17 -0
- package/dist/selfhost-migrations/meta/0063_snapshot.json +7282 -0
- package/dist/selfhost-migrations/meta/_journal.json +7 -0
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
> Deploy email, SMS, and CDN infrastructure to your AWS account.
|
|
4
4
|
|
|
5
|
+
**AI agents:** fetch [wraps.dev/llms-full.txt](https://wraps.dev/llms-full.txt) for the complete documentation in one file, or [wraps.dev/llms.txt](https://wraps.dev/llms.txt) for an agent-oriented index.
|
|
6
|
+
|
|
5
7
|
## What It Creates
|
|
6
8
|
|
|
7
9
|
### Email (`wraps email init`)
|
|
@@ -502,7 +504,7 @@ DMARC (TXT)
|
|
|
502
504
|
Policy for how receivers handle emails failing DKIM/SPF checks
|
|
503
505
|
|
|
504
506
|
TXT _dmarc.example.com
|
|
505
|
-
→ v=DMARC1; p=quarantine; rua=mailto:postmaster@example.com
|
|
507
|
+
→ v=DMARC1; p=quarantine; sp=quarantine; np=reject; rua=mailto:postmaster@example.com
|
|
506
508
|
```
|
|
507
509
|
|
|
508
510
|
## Hosting Provider Integration
|
package/dist/api-lambda.zip
CHANGED
|
Binary file
|
package/dist/cli.js
CHANGED
|
@@ -822,7 +822,7 @@ function pulumiErrorToWrapsError(code, iamAction, service, resourceName, resourc
|
|
|
822
822
|
);
|
|
823
823
|
}
|
|
824
824
|
}
|
|
825
|
-
var WrapsError, errors;
|
|
825
|
+
var WrapsError, CREDENTIAL_OPTIONS, errors;
|
|
826
826
|
var init_errors = __esm({
|
|
827
827
|
"src/utils/shared/errors.ts"() {
|
|
828
828
|
"use strict";
|
|
@@ -838,11 +838,12 @@ var init_errors = __esm({
|
|
|
838
838
|
this.name = "WrapsError";
|
|
839
839
|
}
|
|
840
840
|
};
|
|
841
|
+
CREDENTIAL_OPTIONS = "Wraps couldn't find working AWS credentials. Any of these work:\n\nAWS SSO:\n aws configure sso\n aws sso login\n\nIAM access keys:\n aws configure\n\nEnvironment variables:\n export AWS_ACCESS_KEY_ID=<your-key>\n export AWS_SECRET_ACCESS_KEY=<your-secret>\n\nExisting profile:\n export AWS_PROFILE=<profile-name>";
|
|
841
842
|
errors = {
|
|
842
843
|
noAWSCredentials: () => new WrapsError(
|
|
843
844
|
"AWS credentials not found",
|
|
844
845
|
"NO_AWS_CREDENTIALS",
|
|
845
|
-
|
|
846
|
+
CREDENTIAL_OPTIONS,
|
|
846
847
|
"https://wraps.dev/docs/guides/aws-setup"
|
|
847
848
|
),
|
|
848
849
|
stackExists: (stackName) => new WrapsError(
|
|
@@ -966,7 +967,7 @@ Or configure a new profile:
|
|
|
966
967
|
credentialsFileMissing: () => new WrapsError(
|
|
967
968
|
"AWS credentials file not found",
|
|
968
969
|
"CREDENTIALS_FILE_MISSING",
|
|
969
|
-
|
|
970
|
+
CREDENTIAL_OPTIONS,
|
|
970
971
|
"https://wraps.dev/docs/guides/aws-setup/permissions"
|
|
971
972
|
),
|
|
972
973
|
accessKeyInvalid: () => new WrapsError(
|
|
@@ -1191,22 +1192,27 @@ async function validateAWSCredentials() {
|
|
|
1191
1192
|
async function validateAWSCredentialsWithDetails() {
|
|
1192
1193
|
const state = await detectAWSState();
|
|
1193
1194
|
const warnings = [];
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1195
|
+
const hasStaticEnvCredentials = Boolean(
|
|
1196
|
+
process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY
|
|
1197
|
+
);
|
|
1198
|
+
if (!hasStaticEnvCredentials) {
|
|
1199
|
+
if (state.sso.configured && state.sso.tokenStatus?.expired) {
|
|
1200
|
+
const profile = state.sso.activeProfile?.name;
|
|
1201
|
+
throw errors.ssoSessionExpired(profile);
|
|
1202
|
+
}
|
|
1203
|
+
if (state.sso.configured && state.sso.tokenStatus?.valid && state.sso.tokenStatus.minutesRemaining !== null && state.sso.tokenStatus.minutesRemaining < 15) {
|
|
1204
|
+
const minutes = state.sso.tokenStatus.minutesRemaining;
|
|
1205
|
+
const loginCmd = getSSOLoginCommand(state.sso.activeProfile?.name);
|
|
1206
|
+
warnings.push(
|
|
1207
|
+
`SSO session expires in ${minutes} minute${minutes !== 1 ? "s" : ""}. Run "${loginCmd}" to refresh.`
|
|
1208
|
+
);
|
|
1209
|
+
}
|
|
1210
|
+
const currentProfile = getCurrentProfile();
|
|
1211
|
+
if (currentProfile && currentProfile !== "default") {
|
|
1212
|
+
const availableProfiles = getConfiguredProfiles();
|
|
1213
|
+
if (!availableProfiles.includes(currentProfile)) {
|
|
1214
|
+
throw errors.profileNotFound(currentProfile, availableProfiles);
|
|
1215
|
+
}
|
|
1210
1216
|
}
|
|
1211
1217
|
}
|
|
1212
1218
|
const sts = new STSClient2({ region: "us-east-1" });
|
|
@@ -3047,7 +3053,7 @@ async function previewDNSChanges(hostedZoneId, domain, dkimTokens, region, custo
|
|
|
3047
3053
|
});
|
|
3048
3054
|
const dmarcName = `_dmarc.${domain}`;
|
|
3049
3055
|
const dmarcRuaDomain = mailFromDomain || domain;
|
|
3050
|
-
const proposedDMARC = `"v=DMARC1; p=quarantine; rua=mailto:postmaster@${dmarcRuaDomain}"`;
|
|
3056
|
+
const proposedDMARC = `"v=DMARC1; p=quarantine; sp=quarantine; np=reject; rua=mailto:postmaster@${dmarcRuaDomain}"`;
|
|
3051
3057
|
const existingDMARC = findExistingRecord(existingRecords, dmarcName, "TXT");
|
|
3052
3058
|
const existingDMARCValue = getRecordValue(existingDMARC);
|
|
3053
3059
|
let dmarcStatus = "new";
|
|
@@ -3209,7 +3215,7 @@ async function createSelectedDNSRecords(hostedZoneId, domain, dkimTokens, region
|
|
|
3209
3215
|
TTL: 1800,
|
|
3210
3216
|
ResourceRecords: [
|
|
3211
3217
|
{
|
|
3212
|
-
Value: `"v=DMARC1; p=quarantine; rua=mailto:postmaster@${dmarcRuaDomain}"`
|
|
3218
|
+
Value: `"v=DMARC1; p=quarantine; sp=quarantine; np=reject; rua=mailto:postmaster@${dmarcRuaDomain}"`
|
|
3213
3219
|
}
|
|
3214
3220
|
]
|
|
3215
3221
|
}
|
|
@@ -3332,7 +3338,7 @@ async function createDNSRecords(hostedZoneId, domain, dkimTokens, region, custom
|
|
|
3332
3338
|
TTL: 1800,
|
|
3333
3339
|
ResourceRecords: [
|
|
3334
3340
|
{
|
|
3335
|
-
Value: `"v=DMARC1; p=quarantine; rua=mailto:postmaster@${dmarcRuaDomain}"`
|
|
3341
|
+
Value: `"v=DMARC1; p=quarantine; sp=quarantine; np=reject; rua=mailto:postmaster@${dmarcRuaDomain}"`
|
|
3336
3342
|
}
|
|
3337
3343
|
]
|
|
3338
3344
|
}
|
|
@@ -3556,7 +3562,7 @@ var init_cloudflare = __esm({
|
|
|
3556
3562
|
const dmarcSuccess = await this.createRecord(
|
|
3557
3563
|
`_dmarc.${domain}`,
|
|
3558
3564
|
"TXT",
|
|
3559
|
-
`v=DMARC1; p=quarantine; rua=mailto:postmaster@${mailFromDomain || domain}`
|
|
3565
|
+
`v=DMARC1; p=quarantine; sp=quarantine; np=reject; rua=mailto:postmaster@${mailFromDomain || domain}`
|
|
3560
3566
|
);
|
|
3561
3567
|
if (dmarcSuccess) {
|
|
3562
3568
|
recordsCreated++;
|
|
@@ -3942,7 +3948,7 @@ var init_vercel = __esm({
|
|
|
3942
3948
|
const dmarcSuccess = await this.createRecord(
|
|
3943
3949
|
`_dmarc.${domain}`,
|
|
3944
3950
|
"TXT",
|
|
3945
|
-
`v=DMARC1; p=quarantine; rua=mailto:postmaster@${mailFromDomain || domain}`
|
|
3951
|
+
`v=DMARC1; p=quarantine; sp=quarantine; np=reject; rua=mailto:postmaster@${mailFromDomain || domain}`
|
|
3946
3952
|
);
|
|
3947
3953
|
if (dmarcSuccess) {
|
|
3948
3954
|
recordsCreated++;
|
|
@@ -4211,7 +4217,7 @@ function buildEmailDNSRecords(data) {
|
|
|
4211
4217
|
records.push({
|
|
4212
4218
|
name: `_dmarc.${domain}`,
|
|
4213
4219
|
type: "TXT",
|
|
4214
|
-
value: `v=DMARC1; p=quarantine; rua=mailto:postmaster@${dmarcRuaDomain}`,
|
|
4220
|
+
value: `v=DMARC1; p=quarantine; sp=quarantine; np=reject; rua=mailto:postmaster@${dmarcRuaDomain}`,
|
|
4215
4221
|
category: "dmarc"
|
|
4216
4222
|
});
|
|
4217
4223
|
if (customTrackingDomain) {
|
|
@@ -6263,7 +6269,7 @@ Verification should complete within a few minutes.`,
|
|
|
6263
6269
|
),
|
|
6264
6270
|
"",
|
|
6265
6271
|
pc10.bold("DMARC Record (TXT):"),
|
|
6266
|
-
` ${pc10.cyan(`_dmarc.${domain}`)} ${pc10.dim("TXT")} "v=DMARC1; p=quarantine; rua=mailto:postmaster@${dmarcRuaDomain}"`
|
|
6272
|
+
` ${pc10.cyan(`_dmarc.${domain}`)} ${pc10.dim("TXT")} "v=DMARC1; p=quarantine; sp=quarantine; np=reject; rua=mailto:postmaster@${dmarcRuaDomain}"`
|
|
6267
6273
|
);
|
|
6268
6274
|
if (outputs.mailFromDomain) {
|
|
6269
6275
|
dnsLines.push(
|
|
@@ -6492,7 +6498,7 @@ ${domainStrings.join("\n")}`);
|
|
|
6492
6498
|
),
|
|
6493
6499
|
"",
|
|
6494
6500
|
pc10.bold("DMARC Record (TXT):"),
|
|
6495
|
-
` ${pc10.cyan(`_dmarc.${domain.domain}`)} ${pc10.dim("TXT")} "v=DMARC1; p=quarantine; rua=mailto:postmaster@${dmarcRuaDomain}"`
|
|
6501
|
+
` ${pc10.cyan(`_dmarc.${domain.domain}`)} ${pc10.dim("TXT")} "v=DMARC1; p=quarantine; sp=quarantine; np=reject; rua=mailto:postmaster@${dmarcRuaDomain}"`
|
|
6496
6502
|
);
|
|
6497
6503
|
}
|
|
6498
6504
|
if (domain.mailFromDomain && domain.mailFromStatus !== "SUCCESS") {
|
|
@@ -13020,6 +13026,11 @@ async function init(options) {
|
|
|
13020
13026
|
);
|
|
13021
13027
|
}
|
|
13022
13028
|
const progress = new DeploymentProgress();
|
|
13029
|
+
const identity = await progress.execute(
|
|
13030
|
+
"Validating AWS credentials",
|
|
13031
|
+
async () => validateAWSCredentials()
|
|
13032
|
+
);
|
|
13033
|
+
progress.info(`Connected to AWS account: ${pc12.cyan(identity.accountId)}`);
|
|
13023
13034
|
const wasAutoInstalled = await progress.execute(
|
|
13024
13035
|
"Checking Pulumi CLI installation",
|
|
13025
13036
|
async () => await ensurePulumiInstalled()
|
|
@@ -13027,11 +13038,6 @@ async function init(options) {
|
|
|
13027
13038
|
if (wasAutoInstalled) {
|
|
13028
13039
|
progress.info("Pulumi CLI was automatically installed");
|
|
13029
13040
|
}
|
|
13030
|
-
const identity = await progress.execute(
|
|
13031
|
-
"Validating AWS credentials",
|
|
13032
|
-
async () => validateAWSCredentials()
|
|
13033
|
-
);
|
|
13034
|
-
progress.info(`Connected to AWS account: ${pc12.cyan(identity.accountId)}`);
|
|
13035
13041
|
let provider = options.provider;
|
|
13036
13042
|
if (!provider) {
|
|
13037
13043
|
provider = await promptProvider();
|
|
@@ -15408,6 +15414,9 @@ async function checkDmarc(domain) {
|
|
|
15408
15414
|
valid: false,
|
|
15409
15415
|
policy: null,
|
|
15410
15416
|
subdomainPolicy: null,
|
|
15417
|
+
nonExistentSubdomainPolicy: null,
|
|
15418
|
+
testing: false,
|
|
15419
|
+
psd: null,
|
|
15411
15420
|
percentage: 100,
|
|
15412
15421
|
reportingEnabled: false,
|
|
15413
15422
|
ruaAddresses: [],
|
|
@@ -15460,6 +15469,21 @@ function parseDmarcRecord(record, result) {
|
|
|
15460
15469
|
} else {
|
|
15461
15470
|
result.subdomainPolicy = result.policy;
|
|
15462
15471
|
}
|
|
15472
|
+
const np = tags.get("np");
|
|
15473
|
+
if (np) {
|
|
15474
|
+
if (["none", "quarantine", "reject"].includes(np)) {
|
|
15475
|
+
result.nonExistentSubdomainPolicy = np;
|
|
15476
|
+
} else {
|
|
15477
|
+
result.warnings.push(`Invalid non-existent subdomain policy (np): ${np}`);
|
|
15478
|
+
}
|
|
15479
|
+
}
|
|
15480
|
+
if (tags.get("t") === "y") {
|
|
15481
|
+
result.testing = true;
|
|
15482
|
+
}
|
|
15483
|
+
const psd = tags.get("psd");
|
|
15484
|
+
if (psd && ["y", "n", "u"].includes(psd)) {
|
|
15485
|
+
result.psd = psd;
|
|
15486
|
+
}
|
|
15463
15487
|
const pct = tags.get("pct");
|
|
15464
15488
|
if (pct) {
|
|
15465
15489
|
const pctNum = Number.parseInt(pct, 10);
|
|
@@ -15468,7 +15492,9 @@ function parseDmarcRecord(record, result) {
|
|
|
15468
15492
|
} else {
|
|
15469
15493
|
result.percentage = pctNum;
|
|
15470
15494
|
if (pctNum < 100) {
|
|
15471
|
-
result.warnings.push(
|
|
15495
|
+
result.warnings.push(
|
|
15496
|
+
`pct=${pctNum} is deprecated (DMARCbis receivers ignore it; enforcement is all-or-nothing). Use t=y while testing instead.`
|
|
15497
|
+
);
|
|
15472
15498
|
}
|
|
15473
15499
|
}
|
|
15474
15500
|
}
|
|
@@ -15533,6 +15559,17 @@ function parseDmarcRecord(record, result) {
|
|
|
15533
15559
|
if (result.subdomainPolicy === "none" && result.policy !== "none") {
|
|
15534
15560
|
result.warnings.push("Subdomain policy is less strict than domain policy");
|
|
15535
15561
|
}
|
|
15562
|
+
const enforcing = result.policy === "quarantine" || result.policy === "reject";
|
|
15563
|
+
if (result.testing && result.policy !== "none") {
|
|
15564
|
+
result.warnings.push(
|
|
15565
|
+
"t=y (testing) is set \u2014 receivers will not enforce the policy"
|
|
15566
|
+
);
|
|
15567
|
+
}
|
|
15568
|
+
if (enforcing && !result.nonExistentSubdomainPolicy) {
|
|
15569
|
+
result.warnings.push(
|
|
15570
|
+
"No np= set \u2014 add np=reject to block spoofing from non-existent subdomains (DMARCbis)"
|
|
15571
|
+
);
|
|
15572
|
+
}
|
|
15536
15573
|
if (!result.reportingEnabled) {
|
|
15537
15574
|
result.warnings.push("No aggregate reporting configured (rua=)");
|
|
15538
15575
|
}
|
|
@@ -16686,11 +16723,18 @@ function collectDmarcIssues(dmarc, deductions, bonuses) {
|
|
|
16686
16723
|
reason: "DMARC policy is none (not enforcing)"
|
|
16687
16724
|
});
|
|
16688
16725
|
}
|
|
16689
|
-
if (dmarc.
|
|
16726
|
+
if (dmarc.testing && dmarc.policy !== "none") {
|
|
16690
16727
|
deductions.push({
|
|
16691
16728
|
check: "dmarc",
|
|
16692
16729
|
points: 2,
|
|
16693
|
-
reason:
|
|
16730
|
+
reason: "DMARC testing mode (t=y) \u2014 receivers won't enforce the policy"
|
|
16731
|
+
});
|
|
16732
|
+
}
|
|
16733
|
+
if (dmarc.percentage < 100) {
|
|
16734
|
+
deductions.push({
|
|
16735
|
+
check: "dmarc",
|
|
16736
|
+
points: 1,
|
|
16737
|
+
reason: `DMARC pct=${dmarc.percentage} is deprecated (DMARCbis receivers ignore it; enforcement is inconsistent)`
|
|
16694
16738
|
});
|
|
16695
16739
|
}
|
|
16696
16740
|
if (!dmarc.reportingEnabled) {
|
|
@@ -16707,6 +16751,13 @@ function collectDmarcIssues(dmarc, deductions, bonuses) {
|
|
|
16707
16751
|
reason: "DMARC strict alignment configured"
|
|
16708
16752
|
});
|
|
16709
16753
|
}
|
|
16754
|
+
if ((dmarc.policy === "quarantine" || dmarc.policy === "reject") && dmarc.nonExistentSubdomainPolicy === "reject") {
|
|
16755
|
+
bonuses.push({
|
|
16756
|
+
check: "dmarc",
|
|
16757
|
+
points: 1,
|
|
16758
|
+
reason: "np=reject blocks non-existent subdomain spoofing (DMARCbis)"
|
|
16759
|
+
});
|
|
16760
|
+
}
|
|
16710
16761
|
}
|
|
16711
16762
|
function collectInfraIssues(checks, deductions, bonuses) {
|
|
16712
16763
|
if (checks.mx.exists) {
|
|
@@ -24043,13 +24094,6 @@ async function init2(options) {
|
|
|
24043
24094
|
);
|
|
24044
24095
|
}
|
|
24045
24096
|
const progress = new DeploymentProgress();
|
|
24046
|
-
const wasAutoInstalled = await progress.execute(
|
|
24047
|
-
"Checking Pulumi CLI installation",
|
|
24048
|
-
async () => await ensurePulumiInstalled()
|
|
24049
|
-
);
|
|
24050
|
-
if (wasAutoInstalled) {
|
|
24051
|
-
progress.info("Pulumi CLI was automatically installed");
|
|
24052
|
-
}
|
|
24053
24097
|
const credentialResult = await progress.execute(
|
|
24054
24098
|
"Validating AWS credentials",
|
|
24055
24099
|
async () => validateAWSCredentialsWithDetails()
|
|
@@ -24064,6 +24108,13 @@ async function init2(options) {
|
|
|
24064
24108
|
`Using credentials from: ${pc29.dim(credentialResult.credentialSource)}`
|
|
24065
24109
|
);
|
|
24066
24110
|
}
|
|
24111
|
+
const wasAutoInstalled = await progress.execute(
|
|
24112
|
+
"Checking Pulumi CLI installation",
|
|
24113
|
+
async () => await ensurePulumiInstalled()
|
|
24114
|
+
);
|
|
24115
|
+
if (wasAutoInstalled) {
|
|
24116
|
+
progress.info("Pulumi CLI was automatically installed");
|
|
24117
|
+
}
|
|
24067
24118
|
let provider = options.provider;
|
|
24068
24119
|
if (!provider) {
|
|
24069
24120
|
provider = options.quick ? "other" : await promptProvider();
|
|
@@ -40168,6 +40219,11 @@ async function init3(options) {
|
|
|
40168
40219
|
clack55.intro(pc58.bold("Wraps SMS Infrastructure Setup"));
|
|
40169
40220
|
}
|
|
40170
40221
|
const progress = new DeploymentProgress();
|
|
40222
|
+
const identity = await progress.execute(
|
|
40223
|
+
"Validating AWS credentials",
|
|
40224
|
+
async () => validateAWSCredentials()
|
|
40225
|
+
);
|
|
40226
|
+
progress.info(`Connected to AWS account: ${pc58.cyan(identity.accountId)}`);
|
|
40171
40227
|
const wasAutoInstalled = await progress.execute(
|
|
40172
40228
|
"Checking Pulumi CLI installation",
|
|
40173
40229
|
async () => await ensurePulumiInstalled()
|
|
@@ -40175,11 +40231,6 @@ async function init3(options) {
|
|
|
40175
40231
|
if (wasAutoInstalled) {
|
|
40176
40232
|
progress.info("Pulumi CLI was automatically installed");
|
|
40177
40233
|
}
|
|
40178
|
-
const identity = await progress.execute(
|
|
40179
|
-
"Validating AWS credentials",
|
|
40180
|
-
async () => validateAWSCredentials()
|
|
40181
|
-
);
|
|
40182
|
-
progress.info(`Connected to AWS account: ${pc58.cyan(identity.accountId)}`);
|
|
40183
40234
|
const provider = options.provider || await promptProvider();
|
|
40184
40235
|
const region = options.region || await promptRegion(await getAWSRegion());
|
|
40185
40236
|
let vercelConfig;
|
|
@@ -42712,6 +42763,9 @@ async function support() {
|
|
|
42712
42763
|
` ${pc65.bold("GitHub:")} ${pc65.cyan("https://github.com/wraps-dev/wraps/issues")}`
|
|
42713
42764
|
);
|
|
42714
42765
|
console.log(` ${pc65.bold("Docs:")} ${pc65.cyan("https://wraps.dev/docs")}`);
|
|
42766
|
+
console.log(
|
|
42767
|
+
` ${pc65.bold("AI docs:")} ${pc65.cyan("https://wraps.dev/llms-full.txt")} ${pc65.dim("(complete docs, one file)")}`
|
|
42768
|
+
);
|
|
42715
42769
|
console.log();
|
|
42716
42770
|
console.log(pc65.dim(" Response time: Usually within 24 hours"));
|
|
42717
42771
|
console.log();
|
|
@@ -43363,7 +43417,10 @@ function showHelp() {
|
|
|
43363
43417
|
console.log(` ${pc69.dim("-v, --version")} Show version number
|
|
43364
43418
|
`);
|
|
43365
43419
|
console.log(
|
|
43366
|
-
`Run ${pc69.cyan("wraps <service> <command> --help")} for more information
|
|
43420
|
+
`Run ${pc69.cyan("wraps <service> <command> --help")} for more information.`
|
|
43421
|
+
);
|
|
43422
|
+
console.log(
|
|
43423
|
+
`AI agents: complete docs at ${pc69.cyan("https://wraps.dev/llms-full.txt")}
|
|
43367
43424
|
`
|
|
43368
43425
|
);
|
|
43369
43426
|
}
|