@rozoai/checkout 0.1.12 → 0.1.14
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 +14 -0
- package/llms.txt +14 -1
- package/package.json +1 -1
- package/scripts/dist/cli.js +152 -15
- package/scripts/dist/create-order.js +66 -4
- package/scripts/dist/quote.js +11 -1
- package/scripts/dist/send-evm.js +11 -1
- package/scripts/dist/send-sol.js +11 -1
- package/scripts/dist/status.js +14 -2
package/README.md
CHANGED
|
@@ -116,6 +116,12 @@ the optional `--send` flag signs locally: on Solana it uses the
|
|
|
116
116
|
encrypted JSON keystore whose passphrase is prompted. A raw key in the
|
|
117
117
|
environment (or a gitignored `.env`) still works for unattended automation.
|
|
118
118
|
|
|
119
|
+
Distributing this CLI? `--utm-source <label>` (or `ROZO_CHECKOUT_UTM_SOURCE`) is an optional channel label for reporting; no identity, no privilege.
|
|
120
|
+
|
|
121
|
+
Want ROZO to be able to reach you if a payment needs attention? Add the optional `--email you@example.com` to `pay`.
|
|
122
|
+
|
|
123
|
+
Need help? Email hi@rozo.ai, or reach us on [X](https://x.com/ROZOai) or [Discord](https://discord.gg/EfWejgTbuU).
|
|
124
|
+
|
|
119
125
|
<details>
|
|
120
126
|
<summary><b>Set up a local wallet for <code>--send</code></b> — .env template and per-wallet export steps</summary>
|
|
121
127
|
|
|
@@ -394,6 +400,14 @@ The full list of what this refuses to do, and why, is in
|
|
|
394
400
|
|
|
395
401
|
## Changelog
|
|
396
402
|
|
|
403
|
+
- **0.1.14**: optional `--email <addr>` on `pay` (and on the create-order
|
|
404
|
+
scripts) stores a contact email with the order so ROZO can reach you if the
|
|
405
|
+
payment needs attention. Never required; an invalid address is rejected
|
|
406
|
+
before any order is created. Support channels (hi@rozo.ai, X, Discord) are
|
|
407
|
+
shown at order creation and whenever an order is not clean.
|
|
408
|
+
- **0.1.13**: optional `--utm-source <label>` / `ROZO_CHECKOUT_UTM_SOURCE`
|
|
409
|
+
adds `attribution.utm_source` to created orders so distribution channels can
|
|
410
|
+
be measured. Invalid values are dropped, never failing an order.
|
|
397
411
|
- **0.1.12**: pay Bitrefill invoices. `pay --bitrefill-invoice <id> --to <0x…>
|
|
398
412
|
--amount <USDC> --expires-at <ISO>` creates a Rozo exactOut order through
|
|
399
413
|
mpprouter that delivers the exact USDC on Base straight to the Bitrefill
|
package/llms.txt
CHANGED
|
@@ -25,6 +25,10 @@ calls with every safety rule already enforced:
|
|
|
25
25
|
usdc-ethereum, usdt-polygon, usdc-polygon, usdc-base, usdc-stellar, btc-lightning
|
|
26
26
|
(or raw: --chain 900 --token USDT).
|
|
27
27
|
|
|
28
|
+
Optional: --email <addr> stores a contact email with the order so ROZO can reach
|
|
29
|
+
the user if the payment needs attention. Ask the user once if they are present;
|
|
30
|
+
never required, proceed without it if they decline or no one is there.
|
|
31
|
+
|
|
28
32
|
DEFAULT PATH NEEDS NO KEY: `pay` prints a deposit address for the user to pay from
|
|
29
33
|
any wallet. No private key, no environment variable, no configuration. Only the
|
|
30
34
|
optional --send flag signs locally. Its key comes from the first source that
|
|
@@ -117,6 +121,8 @@ private key (EVM and Solana only); every other path is keyless.
|
|
|
117
121
|
-d '{"url":"https://payments.coinbase.com/payment-links/pl_XXXX",
|
|
118
122
|
"source":{"chainId":"900","tokenSymbol":"USDT"}}'
|
|
119
123
|
-> { ok, reused, linkId, rozoPaymentId, paymentLink, expiresAt, source, ... }
|
|
124
|
+
Optional "email":"you@example.com" in the body stores a contact email with a
|
|
125
|
+
newly created order (a malformed value returns 400 INVALID_EMAIL).
|
|
120
126
|
WARNING: if an unexpired order already exists for this link it is REUSED as-is,
|
|
121
127
|
even with a different chain/token than you asked for. Always verify the returned
|
|
122
128
|
source matches your request; abort on mismatch.
|
|
@@ -162,4 +168,11 @@ private key (EVM and Solana only); every other path is keyless.
|
|
|
162
168
|
|
|
163
169
|
409 = link already used · 410 = link expired · UNSUPPORTED_SOURCE = the response's
|
|
164
170
|
"supported" object lists valid (chainId, tokenSymbol) pairs (Lightning is supported
|
|
165
|
-
but omitted from that list) · RATE_LIMITED = per-IP creation cap, retry later
|
|
171
|
+
but omitted from that list) · RATE_LIMITED = per-IP creation cap, retry later ·
|
|
172
|
+
INVALID_EMAIL = the optional email is malformed; fix it or omit it.
|
|
173
|
+
|
|
174
|
+
## Support
|
|
175
|
+
|
|
176
|
+
If an order is stuck or anything goes wrong: email hi@rozo.ai, X
|
|
177
|
+
https://x.com/ROZOai, Discord https://discord.gg/EfWejgTbuU. Include linkId,
|
|
178
|
+
rozoPaymentId and any tx hash.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rozoai/checkout",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Pay OpenRouter, or any Coinbase invoice, with the crypto you already hold: USDT/USDC on Solana, BNB Chain, Ethereum or Polygon, USDC on Base or Stellar, or BTC over Lightning. No Coinbase account.",
|
|
6
6
|
"license": "MIT",
|
package/scripts/dist/cli.js
CHANGED
|
@@ -41445,7 +41445,47 @@ process.on("warning", (warning) => {
|
|
|
41445
41445
|
import readline2 from "node:readline";
|
|
41446
41446
|
import { createRequire as createRequire2 } from "node:module";
|
|
41447
41447
|
|
|
41448
|
+
// scripts/src/lib/support.mjs
|
|
41449
|
+
var SUPPORT = Object.freeze({
|
|
41450
|
+
email: "hi@rozo.ai",
|
|
41451
|
+
x: "https://x.com/ROZOai",
|
|
41452
|
+
discord: "https://discord.gg/EfWejgTbuU"
|
|
41453
|
+
});
|
|
41454
|
+
var SUPPORT_TEXT = `Need help? Email ${SUPPORT.email}, or reach ROZO on X ${SUPPORT.x} or Discord ${SUPPORT.discord}.`;
|
|
41455
|
+
var CONTACT_EMAIL_MAX_LENGTH = 254;
|
|
41456
|
+
var EMAIL_RE = /^[a-z0-9][^\s@]*@[^\s@.]+(\.[^\s@.]+)+$/;
|
|
41457
|
+
function normalizeContactEmail(raw) {
|
|
41458
|
+
if (raw === void 0 || raw === null) return null;
|
|
41459
|
+
if (typeof raw !== "string") return void 0;
|
|
41460
|
+
const email = raw.trim().toLowerCase();
|
|
41461
|
+
if (email.length === 0) return null;
|
|
41462
|
+
if (email.length > CONTACT_EMAIL_MAX_LENGTH) return void 0;
|
|
41463
|
+
if (/[\x00-\x1f\x7f]/.test(email) || !EMAIL_RE.test(email)) return void 0;
|
|
41464
|
+
return email;
|
|
41465
|
+
}
|
|
41466
|
+
function contactEmailAttached(email, reused) {
|
|
41467
|
+
return Boolean(email) && !reused;
|
|
41468
|
+
}
|
|
41469
|
+
function maskEmail(email) {
|
|
41470
|
+
const at = String(email).lastIndexOf("@");
|
|
41471
|
+
if (at <= 0) return "***";
|
|
41472
|
+
return `${email[0]}***${email.slice(at)}`;
|
|
41473
|
+
}
|
|
41474
|
+
function contactEmailFromArgs(args) {
|
|
41475
|
+
if (args.email === void 0) return null;
|
|
41476
|
+
const email = args.email === true ? void 0 : normalizeContactEmail(String(args.email));
|
|
41477
|
+
if (email === void 0) {
|
|
41478
|
+
const err = new Error(
|
|
41479
|
+
"--email must be a valid email address, for example name@example.com. It is optional: leave it out to continue without one."
|
|
41480
|
+
);
|
|
41481
|
+
err.code = "INVALID_EMAIL";
|
|
41482
|
+
throw err;
|
|
41483
|
+
}
|
|
41484
|
+
return email;
|
|
41485
|
+
}
|
|
41486
|
+
|
|
41448
41487
|
// scripts/src/lib/output.mjs
|
|
41488
|
+
var PUBLIC_SUPPORT_URLS = /* @__PURE__ */ new Set([SUPPORT.x, SUPPORT.discord]);
|
|
41449
41489
|
var EXIT_OK = 0;
|
|
41450
41490
|
var EXIT_ERROR = 1;
|
|
41451
41491
|
var EXIT_USAGE = 2;
|
|
@@ -41457,7 +41497,8 @@ function redact(text) {
|
|
|
41457
41497
|
s = s.replace(/\b[0-9a-fA-F]{64}\b/g, "<redacted>");
|
|
41458
41498
|
s = s.replace(/\b[1-9A-HJ-NP-Za-km-z]{80,90}\b/g, "<redacted>");
|
|
41459
41499
|
s = s.replace(/\[(?:\s*\d{1,3}\s*,){40,}\s*\d{1,3}\s*\]/g, "[<redacted>]");
|
|
41460
|
-
s = s.replace(/\b([a-zA-Z][a-zA-Z0-9+.-]*):\/\/([^\s"'<>]+)/g, (
|
|
41500
|
+
s = s.replace(/\b([a-zA-Z][a-zA-Z0-9+.-]*):\/\/([^\s"'<>]+)/g, (m, scheme, rest) => {
|
|
41501
|
+
if (PUBLIC_SUPPORT_URLS.has(m.replace(/[.,;:!?)]+$/, ""))) return m;
|
|
41461
41502
|
const withoutUserinfo = rest.includes("@") ? rest.slice(rest.indexOf("@") + 1) : rest;
|
|
41462
41503
|
const host = withoutUserinfo.split(/[/?#]/)[0];
|
|
41463
41504
|
const hadMore = withoutUserinfo.length > host.length;
|
|
@@ -41704,6 +41745,14 @@ function isSupportedSource(chainId, tokenSymbol) {
|
|
|
41704
41745
|
return SUPPORTED_SOURCES.some((s) => s.chainId === cid && s.tokens.includes(sym));
|
|
41705
41746
|
}
|
|
41706
41747
|
|
|
41748
|
+
// scripts/src/lib/utm.mjs
|
|
41749
|
+
function normalizeUtmSource(value) {
|
|
41750
|
+
if (typeof value !== "string") return null;
|
|
41751
|
+
const s = value.trim().toLowerCase();
|
|
41752
|
+
if (!s || s.length > 100 || !/^[a-z0-9][a-z0-9._-]*$/.test(s)) return null;
|
|
41753
|
+
return s;
|
|
41754
|
+
}
|
|
41755
|
+
|
|
41707
41756
|
// scripts/src/lib/cli-args.mjs
|
|
41708
41757
|
var COMMANDS = ["pay", "quote", "status", "help", "version"];
|
|
41709
41758
|
var CHAIN_ALIASES = {
|
|
@@ -41834,7 +41883,11 @@ var VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
|
41834
41883
|
"to",
|
|
41835
41884
|
"amount",
|
|
41836
41885
|
"expires-at",
|
|
41837
|
-
"provider"
|
|
41886
|
+
"provider",
|
|
41887
|
+
// Optional channel label for reporting (pay, quote)
|
|
41888
|
+
"utm-source",
|
|
41889
|
+
// Optional contact email so ROZO can reach the payer (pay)
|
|
41890
|
+
"email"
|
|
41838
41891
|
]);
|
|
41839
41892
|
function resolveFromPreset(value) {
|
|
41840
41893
|
try {
|
|
@@ -41903,9 +41956,19 @@ function parseCliArgs(argv) {
|
|
|
41903
41956
|
}
|
|
41904
41957
|
const target = positional[1];
|
|
41905
41958
|
const json = Boolean(flags.json);
|
|
41959
|
+
let utmSource;
|
|
41960
|
+
if (flags["utm-source"] !== void 0 && (command === "pay" || command === "quote")) {
|
|
41961
|
+
utmSource = normalizeUtmSource(String(flags["utm-source"]));
|
|
41962
|
+
if (!utmSource) {
|
|
41963
|
+
throw new CliError(
|
|
41964
|
+
"BAD_VALUE",
|
|
41965
|
+
'--utm-source must be 1-100 characters of a-z, 0-9, ".", "_" or "-", starting with a letter or digit.'
|
|
41966
|
+
);
|
|
41967
|
+
}
|
|
41968
|
+
}
|
|
41906
41969
|
if (command === "quote") {
|
|
41907
41970
|
if (!target) throw new CliError("MISSING_TARGET", "Usage: rozo-checkout quote <coinbase-link>");
|
|
41908
|
-
return { command, target, json };
|
|
41971
|
+
return { command, target, json, ...utmSource ? { utmSource } : {} };
|
|
41909
41972
|
}
|
|
41910
41973
|
if (command === "status") {
|
|
41911
41974
|
if (!target) {
|
|
@@ -41981,6 +42044,16 @@ function parseCliArgs(argv) {
|
|
|
41981
42044
|
} else {
|
|
41982
42045
|
source = null;
|
|
41983
42046
|
}
|
|
42047
|
+
let email = null;
|
|
42048
|
+
if (flags.email !== void 0) {
|
|
42049
|
+
email = normalizeContactEmail(String(flags.email));
|
|
42050
|
+
if (email === void 0) {
|
|
42051
|
+
throw new CliError(
|
|
42052
|
+
"INVALID_EMAIL",
|
|
42053
|
+
"--email must be a valid email address, for example name@example.com. It is optional: leave it out to continue without one."
|
|
42054
|
+
);
|
|
42055
|
+
}
|
|
42056
|
+
}
|
|
41984
42057
|
const timeout = flags.timeout === void 0 ? 900 : Number(flags.timeout);
|
|
41985
42058
|
if (!Number.isFinite(timeout) || timeout < 0) {
|
|
41986
42059
|
throw new CliError("BAD_VALUE", "--timeout must be a non-negative number of seconds.");
|
|
@@ -42000,7 +42073,9 @@ function parseCliArgs(argv) {
|
|
|
42000
42073
|
payer: flags.payer,
|
|
42001
42074
|
fresh: flags.fresh === true,
|
|
42002
42075
|
keyfile: flags.keyfile,
|
|
42003
|
-
envFile: flags["env-file"]
|
|
42076
|
+
envFile: flags["env-file"],
|
|
42077
|
+
...utmSource ? { utmSource } : {},
|
|
42078
|
+
...email ? { email } : {}
|
|
42004
42079
|
};
|
|
42005
42080
|
}
|
|
42006
42081
|
var HELP = `rozo-checkout \u2014 pay a Coinbase Payment Link or a Bitrefill invoice
|
|
@@ -42050,6 +42125,10 @@ OPTIONS
|
|
|
42050
42125
|
gets signed.
|
|
42051
42126
|
--fresh ignore the remembered wallet address and coin for this run
|
|
42052
42127
|
--rpc <url> override the RPC endpoint for --send
|
|
42128
|
+
--utm-source <s> optional channel label for reporting; no identity, no
|
|
42129
|
+
privilege. Overrides ROZO_CHECKOUT_UTM_SOURCE.
|
|
42130
|
+
--email <addr> optional. A contact email stored with the order so ROZO can
|
|
42131
|
+
reach you if the payment needs attention. Never required.
|
|
42053
42132
|
--help, -h this text
|
|
42054
42133
|
--version, -v print the version
|
|
42055
42134
|
|
|
@@ -42057,11 +42136,15 @@ EXAMPLES
|
|
|
42057
42136
|
npx @rozoai/checkout pay https://payments.coinbase.com/payment-links/pl_01...
|
|
42058
42137
|
npx @rozoai/checkout pay https://payments.coinbase.com/payment-links/pl_01... --with usdt-solana
|
|
42059
42138
|
npx @rozoai/checkout pay pl_01... --with btc-lightning
|
|
42139
|
+
npx @rozoai/checkout pay pl_01... --with usdt-solana --email you@example.com
|
|
42060
42140
|
npx @rozoai/checkout pay --bitrefill-invoice 8f3k2 --to 0xAbC\u2026123 --amount 7.90 --expires-at 2026-10-01T12:00:00Z --with usdc-stellar
|
|
42061
42141
|
npx @rozoai/checkout status 11111111-2222-4333-8444-555555555555
|
|
42062
42142
|
|
|
42063
42143
|
Creating an order moves no money; an unfunded order simply expires. Nothing is
|
|
42064
|
-
paid until you confirm, and --send is required before anything is signed
|
|
42144
|
+
paid until you confirm, and --send is required before anything is signed.
|
|
42145
|
+
|
|
42146
|
+
SUPPORT
|
|
42147
|
+
Email hi@rozo.ai, X https://x.com/ROZOai, Discord https://discord.gg/EfWejgTbuU`;
|
|
42065
42148
|
|
|
42066
42149
|
// scripts/src/lib/wallet-check.mjs
|
|
42067
42150
|
var INTENT_API_BASE = process.env.ROZO_CHECKOUT_INTENT_API || "https://intentapi.rozo.ai";
|
|
@@ -54489,22 +54572,30 @@ var PKG_VERSION = (() => {
|
|
|
54489
54572
|
var MPP_BASE = process.env.ROZO_CHECKOUT_MPP_BASE || "https://apiserver.mpprouter.dev/v1/services/rozo-agent-api";
|
|
54490
54573
|
var CLIENT_LABEL = `rozo-checkout-cli/${PKG_VERSION}`;
|
|
54491
54574
|
var ATTRIBUTION_CLIENT = `rozo-checkout-skill/${PKG_VERSION}`;
|
|
54575
|
+
function buildAttribution({ utmSource } = {}) {
|
|
54576
|
+
const raw = utmSource !== void 0 ? utmSource : process.env.ROZO_CHECKOUT_UTM_SOURCE;
|
|
54577
|
+
const utm_source = normalizeUtmSource(raw);
|
|
54578
|
+
return { client: ATTRIBUTION_CLIENT, ...utm_source ? { utm_source } : {} };
|
|
54579
|
+
}
|
|
54492
54580
|
var INTENTS_BASE = process.env.ROZO_CHECKOUT_INTENTS_BASE || "https://intentapiv4.rozo.ai/functions/v1/payment-api";
|
|
54493
54581
|
async function quoteInvoice({ url, linkId }) {
|
|
54494
54582
|
const body = url ? { url } : { payment_id: linkId };
|
|
54495
54583
|
return postJson(`${MPP_BASE}/quote-invoice`, body);
|
|
54496
54584
|
}
|
|
54497
|
-
async function createInvoice({ url, linkId, source, quoteReceipt }) {
|
|
54585
|
+
async function createInvoice({ url, linkId, source, quoteReceipt, utmSource, email }) {
|
|
54498
54586
|
const body = {
|
|
54499
54587
|
...url ? { url } : { payment_id: linkId },
|
|
54500
54588
|
source: { chainId: String(source.chainId), tokenSymbol: source.tokenSymbol },
|
|
54501
54589
|
...quoteReceipt ? { quoteReceipt } : {},
|
|
54502
54590
|
client: CLIENT_LABEL,
|
|
54503
|
-
attribution: {
|
|
54591
|
+
attribution: buildAttribution({ utmSource }),
|
|
54592
|
+
// Optional payer contact email (validated by the caller). Omitted, never
|
|
54593
|
+
// sent empty, when the user gave none.
|
|
54594
|
+
...email ? { email } : {}
|
|
54504
54595
|
};
|
|
54505
54596
|
return postJson(`${MPP_BASE}/create-invoice`, body);
|
|
54506
54597
|
}
|
|
54507
|
-
async function createBitrefillInvoice({ invoice, source }) {
|
|
54598
|
+
async function createBitrefillInvoice({ invoice, source, utmSource, email }) {
|
|
54508
54599
|
const body = {
|
|
54509
54600
|
provider: "bitrefill",
|
|
54510
54601
|
bitrefill: {
|
|
@@ -54515,7 +54606,8 @@ async function createBitrefillInvoice({ invoice, source }) {
|
|
|
54515
54606
|
},
|
|
54516
54607
|
source: { chainId: String(source.chainId), tokenSymbol: source.tokenSymbol },
|
|
54517
54608
|
client: CLIENT_LABEL,
|
|
54518
|
-
attribution: {
|
|
54609
|
+
attribution: buildAttribution({ utmSource }),
|
|
54610
|
+
...email ? { email } : {}
|
|
54519
54611
|
};
|
|
54520
54612
|
return postJson(`${MPP_BASE}/create-invoice`, body);
|
|
54521
54613
|
}
|
|
@@ -55085,6 +55177,7 @@ async function main2(argv) {
|
|
|
55085
55177
|
}
|
|
55086
55178
|
const requested = { chainId, tokenSymbol };
|
|
55087
55179
|
const confirmed = Boolean(args.confirm);
|
|
55180
|
+
const email = contactEmailFromArgs(args);
|
|
55088
55181
|
let blacklist;
|
|
55089
55182
|
try {
|
|
55090
55183
|
blacklist = loadBlacklist();
|
|
@@ -55101,7 +55194,9 @@ async function main2(argv) {
|
|
|
55101
55194
|
const created = await createInvoice({
|
|
55102
55195
|
url: String(url),
|
|
55103
55196
|
source: requested,
|
|
55104
|
-
quoteReceipt
|
|
55197
|
+
quoteReceipt,
|
|
55198
|
+
utmSource: typeof args["utm-source"] === "string" ? args["utm-source"] : void 0,
|
|
55199
|
+
email
|
|
55105
55200
|
});
|
|
55106
55201
|
if (!created?.rozoPaymentId) {
|
|
55107
55202
|
throw new SkillError("CREATE_FAILED", "create-invoice returned no rozoPaymentId.", {
|
|
@@ -55258,6 +55353,13 @@ async function main2(argv) {
|
|
|
55258
55353
|
success: true,
|
|
55259
55354
|
step: "create-order",
|
|
55260
55355
|
confirmed,
|
|
55356
|
+
// True only when an email went out with a NEW order. A reused order keeps
|
|
55357
|
+
// whatever email it was first created with (the router never rebinds), so
|
|
55358
|
+
// reporting true there would be a false promise. The address itself is
|
|
55359
|
+
// never echoed. Note the --confirm run always sees the order as reused;
|
|
55360
|
+
// the CLI carries the first run's value forward.
|
|
55361
|
+
contactEmailProvided: contactEmailAttached(email, created.reused),
|
|
55362
|
+
support: SUPPORT,
|
|
55261
55363
|
reused: Boolean(created.reused),
|
|
55262
55364
|
reusedNote: created.reused ? `An existing unpaid order for this link was reused (${rozoPaymentId}), valid for another ${formatRemaining(expiry.msRemaining)}. Nothing new was created.` : null,
|
|
55263
55365
|
orderCost: "Creating an order moves no money. An order you never fund simply expires and costs nothing.",
|
|
@@ -55532,6 +55634,7 @@ async function main3(argv) {
|
|
|
55532
55634
|
}
|
|
55533
55635
|
const requested = { chainId, tokenSymbol };
|
|
55534
55636
|
const confirmed = Boolean(args.confirm);
|
|
55637
|
+
const email = contactEmailFromArgs(args);
|
|
55535
55638
|
const rawInvoice = {
|
|
55536
55639
|
invoiceId: args["invoice-id"],
|
|
55537
55640
|
address: args.to,
|
|
@@ -55552,7 +55655,12 @@ async function main3(argv) {
|
|
|
55552
55655
|
let rozoPaymentId;
|
|
55553
55656
|
let resumed = false;
|
|
55554
55657
|
try {
|
|
55555
|
-
created = await createBitrefillInvoice({
|
|
55658
|
+
created = await createBitrefillInvoice({
|
|
55659
|
+
invoice,
|
|
55660
|
+
source: requested,
|
|
55661
|
+
utmSource: typeof args["utm-source"] === "string" ? args["utm-source"] : void 0,
|
|
55662
|
+
email
|
|
55663
|
+
});
|
|
55556
55664
|
} catch (err) {
|
|
55557
55665
|
const existing = duplicateInvoicePaymentId(err);
|
|
55558
55666
|
if (!existing) throw explainRouterError(err);
|
|
@@ -55655,6 +55763,9 @@ async function main3(argv) {
|
|
|
55655
55763
|
step: "create-order",
|
|
55656
55764
|
provider: PROVIDER_BITREFILL,
|
|
55657
55765
|
confirmed,
|
|
55766
|
+
// False on a resumed order: the router does not attach an email to it.
|
|
55767
|
+
contactEmailProvided: contactEmailAttached(email, resumed),
|
|
55768
|
+
support: SUPPORT,
|
|
55658
55769
|
reused: resumed,
|
|
55659
55770
|
reusedNote: resumed ? `An order for this Bitrefill invoice already existed (${rozoPaymentId}); it was resumed. Nothing new was created.` : null,
|
|
55660
55771
|
orderCost: "Creating an order moves no money. An order you never fund simply expires and costs nothing.",
|
|
@@ -55901,7 +56012,9 @@ async function main4(argv) {
|
|
|
55901
56012
|
...result,
|
|
55902
56013
|
history,
|
|
55903
56014
|
guidance,
|
|
55904
|
-
timedOut: unresolved
|
|
56015
|
+
timedOut: unresolved,
|
|
56016
|
+
// Anything short of a clean answer gets a human contact.
|
|
56017
|
+
...failed || unresolved ? { support: SUPPORT } : {}
|
|
55905
56018
|
},
|
|
55906
56019
|
failed ? EXIT_ERROR : unresolved ? EXIT_UNCONFIRMED : 0
|
|
55907
56020
|
);
|
|
@@ -56971,6 +57084,9 @@ function printError(payload) {
|
|
|
56971
57084
|
if (e.message) out(` ${e.message}`);
|
|
56972
57085
|
if (payload?.guidance) out(` ${yellow(payload.guidance)}`);
|
|
56973
57086
|
}
|
|
57087
|
+
function printSupport() {
|
|
57088
|
+
out(` ${dim(SUPPORT_TEXT)}`);
|
|
57089
|
+
}
|
|
56974
57090
|
function printMoneyWarning(payload) {
|
|
56975
57091
|
if (!payload?.moneyDetected) return;
|
|
56976
57092
|
out();
|
|
@@ -57119,6 +57235,7 @@ async function cmdStatus(opts) {
|
|
|
57119
57235
|
}
|
|
57120
57236
|
if (payload.error) {
|
|
57121
57237
|
printError(payload);
|
|
57238
|
+
printSupport();
|
|
57122
57239
|
return exitCode;
|
|
57123
57240
|
}
|
|
57124
57241
|
out();
|
|
@@ -57130,6 +57247,7 @@ async function cmdStatus(opts) {
|
|
|
57130
57247
|
if (payload.payin?.txHash) out(` Pay-in ${payload.payin.txHash}`);
|
|
57131
57248
|
if (payload.guidance) out(` ${payload.escalate ? red(payload.guidance) : dim(payload.guidance)}`);
|
|
57132
57249
|
printMoneyWarning(payload);
|
|
57250
|
+
if (payload.support) printSupport();
|
|
57133
57251
|
out();
|
|
57134
57252
|
return exitCode;
|
|
57135
57253
|
}
|
|
@@ -57204,6 +57322,10 @@ async function cmdPay(opts) {
|
|
|
57204
57322
|
}
|
|
57205
57323
|
const { chainId, tokenSymbol } = opts.source;
|
|
57206
57324
|
const createFlow = opts.bitrefill ? run3 : run2;
|
|
57325
|
+
const utmArgs = [
|
|
57326
|
+
...opts.utmSource ? ["--utm-source", opts.utmSource] : [],
|
|
57327
|
+
...opts.email ? ["--email", opts.email] : []
|
|
57328
|
+
];
|
|
57207
57329
|
const baseArgs = opts.bitrefill ? [
|
|
57208
57330
|
"--invoice-id",
|
|
57209
57331
|
opts.bitrefill.invoiceId,
|
|
@@ -57215,8 +57337,9 @@ async function cmdPay(opts) {
|
|
|
57215
57337
|
"--chain",
|
|
57216
57338
|
chainId,
|
|
57217
57339
|
"--token",
|
|
57218
|
-
tokenSymbol
|
|
57219
|
-
|
|
57340
|
+
tokenSymbol,
|
|
57341
|
+
...utmArgs
|
|
57342
|
+
] : ["--url", opts.target, "--chain", chainId, "--token", tokenSymbol, ...utmArgs];
|
|
57220
57343
|
if (opts.send) {
|
|
57221
57344
|
const family = chainFamily(chainId);
|
|
57222
57345
|
if (family === "evm" || family === "solana") {
|
|
@@ -57235,6 +57358,7 @@ async function cmdPay(opts) {
|
|
|
57235
57358
|
else {
|
|
57236
57359
|
printError(created.payload);
|
|
57237
57360
|
printMoneyWarning(created.payload);
|
|
57361
|
+
printSupport();
|
|
57238
57362
|
}
|
|
57239
57363
|
return created.exitCode;
|
|
57240
57364
|
}
|
|
@@ -57264,6 +57388,12 @@ async function cmdPay(opts) {
|
|
|
57264
57388
|
p.reused ? ` ${dim(`Reusing existing unpaid order ${p.rozoPaymentId} \u2014 nothing new was created.`)}` : ` ${dim(`Order created: ${p.rozoPaymentId}`)}`
|
|
57265
57389
|
);
|
|
57266
57390
|
out(` ${dim("An order you never fund simply expires and costs nothing.")}`);
|
|
57391
|
+
if (opts.email) {
|
|
57392
|
+
out(
|
|
57393
|
+
p.reused ? ` ${dim(`Contact ${maskEmail(opts.email)} was not attached: this order already existed.`)}` : ` ${dim(`Contact ${maskEmail(opts.email)}, so ROZO can reach you if this payment needs attention.`)}`
|
|
57394
|
+
);
|
|
57395
|
+
}
|
|
57396
|
+
printSupport();
|
|
57267
57397
|
out();
|
|
57268
57398
|
out(dim(` The amount you send includes bridge and network fees, so it is`));
|
|
57269
57399
|
out(dim(` normally larger than the invoice.`));
|
|
@@ -57294,10 +57424,12 @@ async function cmdPay(opts) {
|
|
|
57294
57424
|
else {
|
|
57295
57425
|
printError(confirmed.payload);
|
|
57296
57426
|
printMoneyWarning(confirmed.payload);
|
|
57427
|
+
printSupport();
|
|
57297
57428
|
}
|
|
57298
57429
|
return confirmed.exitCode;
|
|
57299
57430
|
}
|
|
57300
57431
|
const deposit = confirmed.payload.deposit;
|
|
57432
|
+
confirmed.payload.contactEmailProvided = Boolean(p.contactEmailProvided);
|
|
57301
57433
|
if (!opts.json) {
|
|
57302
57434
|
out(` ${dim(`Confirming order ${rozoPaymentId} \u2014 no second order was created.`)}`);
|
|
57303
57435
|
}
|
|
@@ -57333,6 +57465,7 @@ async function cmdPay(opts) {
|
|
|
57333
57465
|
} else if (!sent.payload.success) {
|
|
57334
57466
|
printError(sent.payload);
|
|
57335
57467
|
printMoneyWarning(sent.payload);
|
|
57468
|
+
printSupport();
|
|
57336
57469
|
return sent.exitCode;
|
|
57337
57470
|
} else if (opts.dryRun) {
|
|
57338
57471
|
if (sent.payload.keySource) out(` ${dim(`would sign with ${sent.payload.keySource}`)}`);
|
|
@@ -57382,6 +57515,7 @@ async function cmdPay(opts) {
|
|
|
57382
57515
|
out(` ${watched.payload.escalate ? red(watched.payload.guidance) : dim(watched.payload.guidance)}`);
|
|
57383
57516
|
}
|
|
57384
57517
|
printMoneyWarning(watched.payload);
|
|
57518
|
+
if (watched.payload.state !== "settled") printSupport();
|
|
57385
57519
|
out();
|
|
57386
57520
|
return watched.exitCode;
|
|
57387
57521
|
}
|
|
@@ -57465,6 +57599,9 @@ async function main7() {
|
|
|
57465
57599
|
main7().then((code) => process.exit(code ?? EXIT_OK)).catch((err) => {
|
|
57466
57600
|
const payload = formatFailure(err);
|
|
57467
57601
|
if (process.argv.includes("--json")) printJson(payload);
|
|
57468
|
-
else
|
|
57602
|
+
else {
|
|
57603
|
+
printError(payload);
|
|
57604
|
+
printSupport();
|
|
57605
|
+
}
|
|
57469
57606
|
process.exit(EXIT_ERROR);
|
|
57470
57607
|
});
|
|
@@ -6,7 +6,42 @@ const require = __rozoCreateRequire(import.meta.url);
|
|
|
6
6
|
const __filename = __rozoFileURLToPath(import.meta.url);
|
|
7
7
|
const __dirname = __rozoDirname(__filename);
|
|
8
8
|
|
|
9
|
+
// scripts/src/lib/support.mjs
|
|
10
|
+
var SUPPORT = Object.freeze({
|
|
11
|
+
email: "hi@rozo.ai",
|
|
12
|
+
x: "https://x.com/ROZOai",
|
|
13
|
+
discord: "https://discord.gg/EfWejgTbuU"
|
|
14
|
+
});
|
|
15
|
+
var SUPPORT_TEXT = `Need help? Email ${SUPPORT.email}, or reach ROZO on X ${SUPPORT.x} or Discord ${SUPPORT.discord}.`;
|
|
16
|
+
var CONTACT_EMAIL_MAX_LENGTH = 254;
|
|
17
|
+
var EMAIL_RE = /^[a-z0-9][^\s@]*@[^\s@.]+(\.[^\s@.]+)+$/;
|
|
18
|
+
function normalizeContactEmail(raw) {
|
|
19
|
+
if (raw === void 0 || raw === null) return null;
|
|
20
|
+
if (typeof raw !== "string") return void 0;
|
|
21
|
+
const email = raw.trim().toLowerCase();
|
|
22
|
+
if (email.length === 0) return null;
|
|
23
|
+
if (email.length > CONTACT_EMAIL_MAX_LENGTH) return void 0;
|
|
24
|
+
if (/[\x00-\x1f\x7f]/.test(email) || !EMAIL_RE.test(email)) return void 0;
|
|
25
|
+
return email;
|
|
26
|
+
}
|
|
27
|
+
function contactEmailAttached(email, reused) {
|
|
28
|
+
return Boolean(email) && !reused;
|
|
29
|
+
}
|
|
30
|
+
function contactEmailFromArgs(args) {
|
|
31
|
+
if (args.email === void 0) return null;
|
|
32
|
+
const email = args.email === true ? void 0 : normalizeContactEmail(String(args.email));
|
|
33
|
+
if (email === void 0) {
|
|
34
|
+
const err = new Error(
|
|
35
|
+
"--email must be a valid email address, for example name@example.com. It is optional: leave it out to continue without one."
|
|
36
|
+
);
|
|
37
|
+
err.code = "INVALID_EMAIL";
|
|
38
|
+
throw err;
|
|
39
|
+
}
|
|
40
|
+
return email;
|
|
41
|
+
}
|
|
42
|
+
|
|
9
43
|
// scripts/src/lib/output.mjs
|
|
44
|
+
var PUBLIC_SUPPORT_URLS = /* @__PURE__ */ new Set([SUPPORT.x, SUPPORT.discord]);
|
|
10
45
|
var EXIT_OK = 0;
|
|
11
46
|
var EXIT_ERROR = 1;
|
|
12
47
|
var EXIT_USAGE = 2;
|
|
@@ -17,7 +52,8 @@ function redact(text) {
|
|
|
17
52
|
s = s.replace(/\b[0-9a-fA-F]{64}\b/g, "<redacted>");
|
|
18
53
|
s = s.replace(/\b[1-9A-HJ-NP-Za-km-z]{80,90}\b/g, "<redacted>");
|
|
19
54
|
s = s.replace(/\[(?:\s*\d{1,3}\s*,){40,}\s*\d{1,3}\s*\]/g, "[<redacted>]");
|
|
20
|
-
s = s.replace(/\b([a-zA-Z][a-zA-Z0-9+.-]*):\/\/([^\s"'<>]+)/g, (
|
|
55
|
+
s = s.replace(/\b([a-zA-Z][a-zA-Z0-9+.-]*):\/\/([^\s"'<>]+)/g, (m, scheme, rest) => {
|
|
56
|
+
if (PUBLIC_SUPPORT_URLS.has(m.replace(/[.,;:!?)]+$/, ""))) return m;
|
|
21
57
|
const withoutUserinfo = rest.includes("@") ? rest.slice(rest.indexOf("@") + 1) : rest;
|
|
22
58
|
const host = withoutUserinfo.split(/[/?#]/)[0];
|
|
23
59
|
const hadMore = withoutUserinfo.length > host.length;
|
|
@@ -224,6 +260,14 @@ function postJson(url, body, opts) {
|
|
|
224
260
|
return request("POST", url, { ...opts, body });
|
|
225
261
|
}
|
|
226
262
|
|
|
263
|
+
// scripts/src/lib/utm.mjs
|
|
264
|
+
function normalizeUtmSource(value) {
|
|
265
|
+
if (typeof value !== "string") return null;
|
|
266
|
+
const s = value.trim().toLowerCase();
|
|
267
|
+
if (!s || s.length > 100 || !/^[a-z0-9][a-z0-9._-]*$/.test(s)) return null;
|
|
268
|
+
return s;
|
|
269
|
+
}
|
|
270
|
+
|
|
227
271
|
// scripts/src/lib/api.mjs
|
|
228
272
|
var PKG_VERSION = (() => {
|
|
229
273
|
const requireFrom = createRequire(import.meta.url);
|
|
@@ -239,18 +283,26 @@ var PKG_VERSION = (() => {
|
|
|
239
283
|
var MPP_BASE = process.env.ROZO_CHECKOUT_MPP_BASE || "https://apiserver.mpprouter.dev/v1/services/rozo-agent-api";
|
|
240
284
|
var CLIENT_LABEL = `rozo-checkout-cli/${PKG_VERSION}`;
|
|
241
285
|
var ATTRIBUTION_CLIENT = `rozo-checkout-skill/${PKG_VERSION}`;
|
|
286
|
+
function buildAttribution({ utmSource } = {}) {
|
|
287
|
+
const raw = utmSource !== void 0 ? utmSource : process.env.ROZO_CHECKOUT_UTM_SOURCE;
|
|
288
|
+
const utm_source = normalizeUtmSource(raw);
|
|
289
|
+
return { client: ATTRIBUTION_CLIENT, ...utm_source ? { utm_source } : {} };
|
|
290
|
+
}
|
|
242
291
|
var INTENTS_BASE = process.env.ROZO_CHECKOUT_INTENTS_BASE || "https://intentapiv4.rozo.ai/functions/v1/payment-api";
|
|
243
292
|
async function quoteInvoice({ url, linkId }) {
|
|
244
293
|
const body = url ? { url } : { payment_id: linkId };
|
|
245
294
|
return postJson(`${MPP_BASE}/quote-invoice`, body);
|
|
246
295
|
}
|
|
247
|
-
async function createInvoice({ url, linkId, source, quoteReceipt }) {
|
|
296
|
+
async function createInvoice({ url, linkId, source, quoteReceipt, utmSource, email }) {
|
|
248
297
|
const body = {
|
|
249
298
|
...url ? { url } : { payment_id: linkId },
|
|
250
299
|
source: { chainId: String(source.chainId), tokenSymbol: source.tokenSymbol },
|
|
251
300
|
...quoteReceipt ? { quoteReceipt } : {},
|
|
252
301
|
client: CLIENT_LABEL,
|
|
253
|
-
attribution: {
|
|
302
|
+
attribution: buildAttribution({ utmSource }),
|
|
303
|
+
// Optional payer contact email (validated by the caller). Omitted, never
|
|
304
|
+
// sent empty, when the user gave none.
|
|
305
|
+
...email ? { email } : {}
|
|
254
306
|
};
|
|
255
307
|
return postJson(`${MPP_BASE}/create-invoice`, body);
|
|
256
308
|
}
|
|
@@ -1153,6 +1205,7 @@ async function main(argv) {
|
|
|
1153
1205
|
}
|
|
1154
1206
|
const requested = { chainId, tokenSymbol };
|
|
1155
1207
|
const confirmed = Boolean(args.confirm);
|
|
1208
|
+
const email = contactEmailFromArgs(args);
|
|
1156
1209
|
let blacklist;
|
|
1157
1210
|
try {
|
|
1158
1211
|
blacklist = loadBlacklist();
|
|
@@ -1169,7 +1222,9 @@ async function main(argv) {
|
|
|
1169
1222
|
const created = await createInvoice({
|
|
1170
1223
|
url: String(url),
|
|
1171
1224
|
source: requested,
|
|
1172
|
-
quoteReceipt
|
|
1225
|
+
quoteReceipt,
|
|
1226
|
+
utmSource: typeof args["utm-source"] === "string" ? args["utm-source"] : void 0,
|
|
1227
|
+
email
|
|
1173
1228
|
});
|
|
1174
1229
|
if (!created?.rozoPaymentId) {
|
|
1175
1230
|
throw new SkillError("CREATE_FAILED", "create-invoice returned no rozoPaymentId.", {
|
|
@@ -1326,6 +1381,13 @@ async function main(argv) {
|
|
|
1326
1381
|
success: true,
|
|
1327
1382
|
step: "create-order",
|
|
1328
1383
|
confirmed,
|
|
1384
|
+
// True only when an email went out with a NEW order. A reused order keeps
|
|
1385
|
+
// whatever email it was first created with (the router never rebinds), so
|
|
1386
|
+
// reporting true there would be a false promise. The address itself is
|
|
1387
|
+
// never echoed. Note the --confirm run always sees the order as reused;
|
|
1388
|
+
// the CLI carries the first run's value forward.
|
|
1389
|
+
contactEmailProvided: contactEmailAttached(email, created.reused),
|
|
1390
|
+
support: SUPPORT,
|
|
1329
1391
|
reused: Boolean(created.reused),
|
|
1330
1392
|
reusedNote: created.reused ? `An existing unpaid order for this link was reused (${rozoPaymentId}), valid for another ${formatRemaining(expiry.msRemaining)}. Nothing new was created.` : null,
|
|
1331
1393
|
orderCost: "Creating an order moves no money. An order you never fund simply expires and costs nothing.",
|
package/scripts/dist/quote.js
CHANGED
|
@@ -6,7 +6,16 @@ const require = __rozoCreateRequire(import.meta.url);
|
|
|
6
6
|
const __filename = __rozoFileURLToPath(import.meta.url);
|
|
7
7
|
const __dirname = __rozoDirname(__filename);
|
|
8
8
|
|
|
9
|
+
// scripts/src/lib/support.mjs
|
|
10
|
+
var SUPPORT = Object.freeze({
|
|
11
|
+
email: "hi@rozo.ai",
|
|
12
|
+
x: "https://x.com/ROZOai",
|
|
13
|
+
discord: "https://discord.gg/EfWejgTbuU"
|
|
14
|
+
});
|
|
15
|
+
var SUPPORT_TEXT = `Need help? Email ${SUPPORT.email}, or reach ROZO on X ${SUPPORT.x} or Discord ${SUPPORT.discord}.`;
|
|
16
|
+
|
|
9
17
|
// scripts/src/lib/output.mjs
|
|
18
|
+
var PUBLIC_SUPPORT_URLS = /* @__PURE__ */ new Set([SUPPORT.x, SUPPORT.discord]);
|
|
10
19
|
var EXIT_OK = 0;
|
|
11
20
|
var EXIT_ERROR = 1;
|
|
12
21
|
var EXIT_USAGE = 2;
|
|
@@ -17,7 +26,8 @@ function redact(text) {
|
|
|
17
26
|
s = s.replace(/\b[0-9a-fA-F]{64}\b/g, "<redacted>");
|
|
18
27
|
s = s.replace(/\b[1-9A-HJ-NP-Za-km-z]{80,90}\b/g, "<redacted>");
|
|
19
28
|
s = s.replace(/\[(?:\s*\d{1,3}\s*,){40,}\s*\d{1,3}\s*\]/g, "[<redacted>]");
|
|
20
|
-
s = s.replace(/\b([a-zA-Z][a-zA-Z0-9+.-]*):\/\/([^\s"'<>]+)/g, (
|
|
29
|
+
s = s.replace(/\b([a-zA-Z][a-zA-Z0-9+.-]*):\/\/([^\s"'<>]+)/g, (m, scheme, rest) => {
|
|
30
|
+
if (PUBLIC_SUPPORT_URLS.has(m.replace(/[.,;:!?)]+$/, ""))) return m;
|
|
21
31
|
const withoutUserinfo = rest.includes("@") ? rest.slice(rest.indexOf("@") + 1) : rest;
|
|
22
32
|
const host = withoutUserinfo.split(/[/?#]/)[0];
|
|
23
33
|
const hadMore = withoutUserinfo.length > host.length;
|
package/scripts/dist/send-evm.js
CHANGED
|
@@ -9972,7 +9972,16 @@ var init_call = __esm({
|
|
|
9972
9972
|
}
|
|
9973
9973
|
});
|
|
9974
9974
|
|
|
9975
|
+
// scripts/src/lib/support.mjs
|
|
9976
|
+
var SUPPORT = Object.freeze({
|
|
9977
|
+
email: "hi@rozo.ai",
|
|
9978
|
+
x: "https://x.com/ROZOai",
|
|
9979
|
+
discord: "https://discord.gg/EfWejgTbuU"
|
|
9980
|
+
});
|
|
9981
|
+
var SUPPORT_TEXT = `Need help? Email ${SUPPORT.email}, or reach ROZO on X ${SUPPORT.x} or Discord ${SUPPORT.discord}.`;
|
|
9982
|
+
|
|
9975
9983
|
// scripts/src/lib/output.mjs
|
|
9984
|
+
var PUBLIC_SUPPORT_URLS = /* @__PURE__ */ new Set([SUPPORT.x, SUPPORT.discord]);
|
|
9976
9985
|
var EXIT_OK = 0;
|
|
9977
9986
|
var EXIT_ERROR = 1;
|
|
9978
9987
|
var EXIT_UNCONFIRMED = 3;
|
|
@@ -9983,7 +9992,8 @@ function redact(text) {
|
|
|
9983
9992
|
s = s.replace(/\b[0-9a-fA-F]{64}\b/g, "<redacted>");
|
|
9984
9993
|
s = s.replace(/\b[1-9A-HJ-NP-Za-km-z]{80,90}\b/g, "<redacted>");
|
|
9985
9994
|
s = s.replace(/\[(?:\s*\d{1,3}\s*,){40,}\s*\d{1,3}\s*\]/g, "[<redacted>]");
|
|
9986
|
-
s = s.replace(/\b([a-zA-Z][a-zA-Z0-9+.-]*):\/\/([^\s"'<>]+)/g, (
|
|
9995
|
+
s = s.replace(/\b([a-zA-Z][a-zA-Z0-9+.-]*):\/\/([^\s"'<>]+)/g, (m, scheme, rest) => {
|
|
9996
|
+
if (PUBLIC_SUPPORT_URLS.has(m.replace(/[.,;:!?)]+$/, ""))) return m;
|
|
9987
9997
|
const withoutUserinfo = rest.includes("@") ? rest.slice(rest.indexOf("@") + 1) : rest;
|
|
9988
9998
|
const host = withoutUserinfo.split(/[/?#]/)[0];
|
|
9989
9999
|
const hadMore = withoutUserinfo.length > host.length;
|
package/scripts/dist/send-sol.js
CHANGED
|
@@ -32143,7 +32143,16 @@ Message: ${transactionMessage}.
|
|
|
32143
32143
|
}
|
|
32144
32144
|
});
|
|
32145
32145
|
|
|
32146
|
+
// scripts/src/lib/support.mjs
|
|
32147
|
+
var SUPPORT = Object.freeze({
|
|
32148
|
+
email: "hi@rozo.ai",
|
|
32149
|
+
x: "https://x.com/ROZOai",
|
|
32150
|
+
discord: "https://discord.gg/EfWejgTbuU"
|
|
32151
|
+
});
|
|
32152
|
+
var SUPPORT_TEXT = `Need help? Email ${SUPPORT.email}, or reach ROZO on X ${SUPPORT.x} or Discord ${SUPPORT.discord}.`;
|
|
32153
|
+
|
|
32146
32154
|
// scripts/src/lib/output.mjs
|
|
32155
|
+
var PUBLIC_SUPPORT_URLS = /* @__PURE__ */ new Set([SUPPORT.x, SUPPORT.discord]);
|
|
32147
32156
|
var EXIT_OK = 0;
|
|
32148
32157
|
var EXIT_ERROR = 1;
|
|
32149
32158
|
var EXIT_UNCONFIRMED = 3;
|
|
@@ -32154,7 +32163,8 @@ function redact(text) {
|
|
|
32154
32163
|
s = s.replace(/\b[0-9a-fA-F]{64}\b/g, "<redacted>");
|
|
32155
32164
|
s = s.replace(/\b[1-9A-HJ-NP-Za-km-z]{80,90}\b/g, "<redacted>");
|
|
32156
32165
|
s = s.replace(/\[(?:\s*\d{1,3}\s*,){40,}\s*\d{1,3}\s*\]/g, "[<redacted>]");
|
|
32157
|
-
s = s.replace(/\b([a-zA-Z][a-zA-Z0-9+.-]*):\/\/([^\s"'<>]+)/g, (
|
|
32166
|
+
s = s.replace(/\b([a-zA-Z][a-zA-Z0-9+.-]*):\/\/([^\s"'<>]+)/g, (m, scheme, rest) => {
|
|
32167
|
+
if (PUBLIC_SUPPORT_URLS.has(m.replace(/[.,;:!?)]+$/, ""))) return m;
|
|
32158
32168
|
const withoutUserinfo = rest.includes("@") ? rest.slice(rest.indexOf("@") + 1) : rest;
|
|
32159
32169
|
const host = withoutUserinfo.split(/[/?#]/)[0];
|
|
32160
32170
|
const hadMore = withoutUserinfo.length > host.length;
|
package/scripts/dist/status.js
CHANGED
|
@@ -6,7 +6,16 @@ const require = __rozoCreateRequire(import.meta.url);
|
|
|
6
6
|
const __filename = __rozoFileURLToPath(import.meta.url);
|
|
7
7
|
const __dirname = __rozoDirname(__filename);
|
|
8
8
|
|
|
9
|
+
// scripts/src/lib/support.mjs
|
|
10
|
+
var SUPPORT = Object.freeze({
|
|
11
|
+
email: "hi@rozo.ai",
|
|
12
|
+
x: "https://x.com/ROZOai",
|
|
13
|
+
discord: "https://discord.gg/EfWejgTbuU"
|
|
14
|
+
});
|
|
15
|
+
var SUPPORT_TEXT = `Need help? Email ${SUPPORT.email}, or reach ROZO on X ${SUPPORT.x} or Discord ${SUPPORT.discord}.`;
|
|
16
|
+
|
|
9
17
|
// scripts/src/lib/output.mjs
|
|
18
|
+
var PUBLIC_SUPPORT_URLS = /* @__PURE__ */ new Set([SUPPORT.x, SUPPORT.discord]);
|
|
10
19
|
var EXIT_OK = 0;
|
|
11
20
|
var EXIT_ERROR = 1;
|
|
12
21
|
var EXIT_USAGE = 2;
|
|
@@ -18,7 +27,8 @@ function redact(text) {
|
|
|
18
27
|
s = s.replace(/\b[0-9a-fA-F]{64}\b/g, "<redacted>");
|
|
19
28
|
s = s.replace(/\b[1-9A-HJ-NP-Za-km-z]{80,90}\b/g, "<redacted>");
|
|
20
29
|
s = s.replace(/\[(?:\s*\d{1,3}\s*,){40,}\s*\d{1,3}\s*\]/g, "[<redacted>]");
|
|
21
|
-
s = s.replace(/\b([a-zA-Z][a-zA-Z0-9+.-]*):\/\/([^\s"'<>]+)/g, (
|
|
30
|
+
s = s.replace(/\b([a-zA-Z][a-zA-Z0-9+.-]*):\/\/([^\s"'<>]+)/g, (m, scheme, rest) => {
|
|
31
|
+
if (PUBLIC_SUPPORT_URLS.has(m.replace(/[.,;:!?)]+$/, ""))) return m;
|
|
22
32
|
const withoutUserinfo = rest.includes("@") ? rest.slice(rest.indexOf("@") + 1) : rest;
|
|
23
33
|
const host = withoutUserinfo.split(/[/?#]/)[0];
|
|
24
34
|
const hadMore = withoutUserinfo.length > host.length;
|
|
@@ -747,7 +757,9 @@ async function main(argv) {
|
|
|
747
757
|
...result,
|
|
748
758
|
history,
|
|
749
759
|
guidance,
|
|
750
|
-
timedOut: unresolved
|
|
760
|
+
timedOut: unresolved,
|
|
761
|
+
// Anything short of a clean answer gets a human contact.
|
|
762
|
+
...failed || unresolved ? { support: SUPPORT } : {}
|
|
751
763
|
},
|
|
752
764
|
failed ? EXIT_ERROR : unresolved ? EXIT_UNCONFIRMED : 0
|
|
753
765
|
);
|